async def valuation_command(ctx, economy_group="sector"): """Valuation of sectors, industry, country [Finviz]""" d_economy_group = { "sector": "Sector", "industry": "Industry", "basic_materials": "Industry (Basic Materials)", "communication_services": "Industry (Communication Services)", "consumer_cyclical": "Industry (Consumer Cyclical)", "consumer_defensive": "Industry (Consumer Defensive)", "energy": "Industry (Energy)", "financial": "Industry (Financial)", "healthcare": "Industry (Healthcare)", "industrials": "Industry (Industrials)", "real_estate": "Industry (Real Estate)", "technology": "Industry (Technology)", "utilities": "Industry (Utilities)", "country": "Country (U.S. listed stocks only)", "capitalization": "Capitalization", } try: # Debug user input if cfg.DEBUG: logger.debug("!economy.valuation %s", economy_group) # Select default group if economy_group == "": if cfg.DEBUG: logger.debug("Use default economy_group: 'sector'") economy_group = "sector" # Check for argument possible_groups = list(d_economy_group.keys()) if economy_group not in possible_groups: raise Exception( f"Select a valid group from {', '.join(possible_groups)}" # nosec ) group = d_economy_group[economy_group] # Retrieve data df_group = finviz_model.get_valuation_performance_data(group, "valuation") # Debug user output if cfg.DEBUG: logger.debug(df_group.to_string()) # Output data future_column_name = df_group["Name"] df_group = df_group.transpose() df_group.columns = future_column_name df_group.drop("Name") columns = [] initial_str = "Page 0: Overview" i = 1 for col_name in df_group.columns.values: initial_str += f"\nPage {i}: {col_name}" i += 1 columns.append( discord.Embed( title=f"Economy: [Finviz] Valuation {group}", description=initial_str, colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) ) for column in df_group.columns.values: columns.append( discord.Embed( description="```" + df_group[column].fillna("").to_string() + "```", colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) ) await pagination(columns, ctx) except Exception as e: embed = discord.Embed( title="ERROR Economy: [Finviz] Valuation", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def vol_command( ctx, ticker: str = None, expiry: str = "", min_sp: float = None, max_sp: float = None, ): """Options VOL""" try: # Debug if cfg.DEBUG: logger.debug("!stocks.opt.vol %s %s %s %s", ticker, expiry, min_sp, max_sp) # Check for argument if ticker is None: raise Exception("Stock ticker is required") dates = yfinance_model.option_expirations(ticker) if not dates: raise Exception("Stock ticker is invalid") options = yfinance_model.get_option_chain(ticker, expiry) current_price = yfinance_model.get_price(ticker) if min_sp is None: min_strike = 0.75 * current_price else: min_strike = min_sp if max_sp is None: max_strike = 1.25 * current_price else: max_strike = max_sp calls = options.calls puts = options.puts call_v = calls.set_index("strike")["volume"] / 1000 put_v = puts.set_index("strike")["volume"] / 1000 plt.style.use("seaborn") fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI) put_v.plot( x="strike", y="volume", label="Puts", ax=ax, marker="o", ls="-", c="r", ) call_v.plot( x="strike", y="volume", label="Calls", ax=ax, marker="o", ls="-", c="g", ) ax.axvline( current_price, lw=2, c="k", ls="--", label="Current Price", alpha=0.7 ) ax.grid("on") ax.set_xlabel("Strike Price") ax.set_ylabel("Volume (1k) ") ax.set_xlim(min_strike, max_strike) ax.set_title(f"Volume for {ticker.upper()} expiring {expiry}") plt.legend(loc=0) fig.tight_layout(pad=1) imagefile = "opt_vol.png" plt.savefig("opt_vol.png") image = discord.File(imagefile) if cfg.DEBUG: logger.debug("Image: %s", imagefile) title = f"Volume for {ticker.upper()} expiring {expiry}" embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_image(url="attachment://opt_vol.png") embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) os.remove("opt_vol.png") await ctx.send(embed=embed, file=image) except Exception as e: embed = discord.Embed( title="ERROR Options: Volume", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def adosc_command( ctx, ticker="", is_open="False", fast="3", slow="10", start="", end="" ): """Displays chart with chaikin oscillator [Yahoo Finance]""" try: # Debug if cfg.DEBUG: # pylint: disable=logging-too-many-args logger.debug( "!stocks.ta.adosc %s %s %s %s %s", ticker, is_open, fast, slow, start, end, ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) if not fast.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") fast = float(fast) if not slow.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") slow = float(slow) if is_open in ["false", "False", "FALSE", ""]: is_open = False if is_open in ["true", "True", "TRUE"]: is_open = True if is_open not in [True, False]: raise Exception("raw argument has to be true or false") ticker = ticker.upper() df_stock = discordbot.helpers.load(ticker, start) if df_stock.empty: raise Exception("Stock ticker is invalid") # Retrieve Data df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)] # Output Data bar_colors = [ "r" if x[1].Open < x[1].Close else "g" for x in df_stock.iterrows() ] divisor = 1_000_000 df_vol = df_stock["Volume"].dropna() df_vol = df_vol.values / divisor df_ta = volume_model.adosc(df_stock, is_open, fast, slow) df_cal = df_ta.values df_cal = df_cal / divisor fig, axes = plt.subplots( 3, 1, figsize=plot_autoscale(), dpi=PLOT_DPI, ) ax = axes[0] ax.set_title(f"{ticker} AD Oscillator") ax.plot(df_stock.index, df_stock["Adj Close"].values, "fuchsia", lw=1) ax.set_xlim(df_stock.index[0], df_stock.index[-1]) ax.set_ylabel("Price") ax.grid(b=True, which="major", color="#666666", linestyle="-") ax1 = axes[1] ax1.set_ylabel("Volume [M]") ax1.bar( df_stock.index, df_vol, color=bar_colors, alpha=0.8, width=0.3, ) ax1.set_xlim(df_stock.index[0], df_stock.index[-1]) ax2 = axes[2] ax2.set_ylabel("AD Osc [M]") ax2.set_xlabel("Time") ax2.plot(df_ta.index, df_cal, "b", lw=2, label="AD Osc") ax2.set_xlim(df_stock.index[0], df_stock.index[-1]) ax2.grid(b=True, which="major", color="#666666", linestyle="-") plt.gcf().autofmt_xdate() fig.tight_layout(pad=1) plt.legend() plt.savefig("ta_adosc.png") uploaded_image = gst_imgur.upload_image("ta_adosc.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = "Stocks: Accumulation/Distribution Oscillator " + ticker embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("ta_adosc.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stocks: Accumulation/Distribution Oscillator", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def donchian_command(ctx, ticker="", upper_length="25", lower_length="100", start="", end=""): """Displays chart with donchian channel [Yahoo Finance]""" try: # Debug if cfg.DEBUG: logger.debug( "!stocks.ta.donchian %s %s %s %s %s", ticker, upper_length, lower_length, start, end, ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) if not upper_length.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") upper_length = float(upper_length) if not lower_length.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") lower_length = float(lower_length) ticker = ticker.upper() df_stock = discordbot.helpers.load(ticker, start) if df_stock.empty: raise Exception("Stock ticker is invalid") # Retrieve Data df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)] df_ta = volatility_model.donchian(df_stock["High"], df_stock["Low"], upper_length, lower_length) # Output Data fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) ax.plot(df_stock.index, df_stock["Adj Close"].values, color="k", lw=3) ax.plot(df_ta.index, df_ta.iloc[:, 0].values, "b", lw=1.5, label="upper") ax.plot(df_ta.index, df_ta.iloc[:, 1].values, "b", lw=1.5, ls="--") ax.plot(df_ta.index, df_ta.iloc[:, 2].values, "b", lw=1.5, label="lower") ax.set_title(f"{ticker} donchian") ax.set_xlim(df_stock.index[0], df_stock.index[-1]) ax.set_xlabel("Time") ax.set_ylabel("Price ($)") ax.legend( [ticker, df_ta.columns[0], df_ta.columns[1], df_ta.columns[2]]) ax.fill_between( df_ta.index, df_ta.iloc[:, 0].values, df_ta.iloc[:, 2].values, alpha=0.1, color="b", ) ax.grid(b=True, which="major", color="#666666", linestyle="-") plt.gcf().autofmt_xdate() fig.tight_layout(pad=1) plt.legend() plt.savefig("ta_donchian.png") uploaded_image = gst_imgur.upload_image("ta_donchian.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = "Stocks: Donchian-Channels " + ticker embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("ta_donchian.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stocks: Donchian-Channels", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def ad_command(ctx, ticker="", is_open="False", start="", end=""): """Displays chart with accumulation/distribution line [Yahoo Finance]""" try: # Debug if cfg.DEBUG: logger.debug("!stocks.ta.ad %s %s %s %s", ticker, is_open, start, end) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) if is_open in ["false", "False", "FALSE", ""]: is_open = False if is_open in ["true", "True", "TRUE"]: is_open = True if is_open not in [True, False]: raise Exception("is_open argument has to be true or false") ticker = ticker.upper() df_stock = discordbot.helpers.load(ticker, start) if df_stock.empty: raise Exception("Stock ticker is invalid") # Retrieve Data df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)] bar_colors = [ "r" if x[1].Open < x[1].Close else "g" for x in df_stock.iterrows() ] divisor = 1_000_000 df_vol = df_stock["Volume"].dropna() df_vol = df_vol.values / divisor df_ta = volume_model.ad(df_stock, is_open) df_cal = df_ta.values df_cal = df_cal / divisor fig, axes = plt.subplots( 3, 1, gridspec_kw={"height_ratios": [2, 1, 1]}, figsize=plot_autoscale(), dpi=PLOT_DPI, ) ax = axes[0] ax.plot(df_stock.index, df_stock["Adj Close"].values, "k", lw=2) ax.set_title(f"{ticker} AD") ax.set_xlim(df_stock.index[0], df_stock.index[-1]) ax.set_ylabel("Price") ax.grid(b=True, which="major", color="#666666", linestyle="-") ax2 = axes[1] ax2.set_ylabel("Volume [M]") ax2.bar( df_stock.index, df_vol, color=bar_colors, alpha=0.8, width=0.3, ) ax2.set_xlim(df_stock.index[0], df_stock.index[-1]) ax3 = axes[2] ax3.set_ylabel("A/D [M]") ax3.set_xlabel("Time") ax3.plot(df_ta.index, df_cal, "b", lw=1) ax3.set_xlim(df_stock.index[0], df_stock.index[-1]) ax3.axhline(0, linewidth=2, color="k", ls="--") ax3.grid(b=True, which="major", color="#666666", linestyle="-") plt.gcf().autofmt_xdate() fig.tight_layout(pad=1) plt.savefig("ta_ad.png") uploaded_image = gst_imgur.upload_image("ta_ad.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = "Stocks: Accumulation/Distribution Line " + ticker embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("ta_ad.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stocks: Accumulation/Distribution Line", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def shorted_command(ctx, num="10"): """Show most shorted stocks [Yahoo Finance]""" try: # Debug user input if cfg.DEBUG: logger.debug("!stocks.dps.shorted %s", num) # Check for argument if not num.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") num = int(num) if num < 0: raise Exception("Number has to be above 0") # Retrieve data df = yahoofinance_model.get_most_shorted().head(num) # Debug user output if cfg.DEBUG: logger.debug(df.to_string()) # Output data df.dropna(how="all", axis=1, inplace=True) df = df.replace(float("NaN"), "") future_column_name = df["Symbol"] df = df.transpose() df.columns = future_column_name df.drop("Symbol") columns = [] initial_str = "Page 0: Overview" i = 1 for col_name in df.columns.values: initial_str += f"\nPage {i}: {col_name}" i += 1 columns.append( discord.Embed( title="Stocks: [Yahoo Finance] Most Shorted", description=initial_str, colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) for column in df.columns.values: columns.append( discord.Embed( description="```" + df[column].fillna("").to_string() + "```", colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) await pagination(columns, ctx) except Exception as e: embed = discord.Embed( title="ERROR Stocks: [Yahoo Finance] Most Shorted", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def arktrades_command(ctx, ticker="", num=""): """Displays trades made by ark [cathiesark.com]""" try: # Debug user input if cfg.DEBUG: logger.debug("!stocks.dd.arktrades %s", ticker) if num == "": pass else: if not num.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") num = int(num) if ticker == "": raise Exception("A ticker is required") ark_holdings = ark_model.get_ark_trades_by_ticker(ticker) if ark_holdings.empty: raise Exception( "Issue getting data from cathiesark.com. Likely no trades found.\n" ) ark_holdings = ark_holdings.drop(columns=["ticker"]) ark_holdings["Total"] = ark_holdings["Total"] / 1_000_000 ark_holdings.rename(columns={ "Close": "Close ($)", "Total": "Total ($1M)" }, inplace=True) ark_holdings.index = pd.Series( ark_holdings.index).apply(lambda x: x.strftime("%Y-%m-%d")) if num == "": ark_holdings_str = ark_holdings.to_string() else: ark_holdings_str = ark_holdings.head(num).to_string() if len(ark_holdings_str) <= 4000: embed = discord.Embed( title=f"Stocks: [cathiesark.com] {ticker} Trades by Ark", description="```" + ark_holdings_str + "```", colour=cfg.COLOR, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed) else: i = 0 str_start = 0 str_end = 4000 columns = [] while i <= len(ark_holdings_str) / 4000: columns.append( discord.Embed( title= f"Stocks: [cathiesark.com] {ticker} Trades by Ark", description="```" + ark_holdings_str[str_start:str_end] + "```", colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) str_end = str_start str_start += 4000 i += 1 await pagination(columns, ctx) except Exception as e: embed = discord.Embed( title=f"ERROR Stocks: [cathiesark.com] {ticker} Trades by Ark", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def rsi_command(ctx, ticker="", length="14", scalar="100", drift="1", start="", end=""): """Displays chart with relative strength index [Yahoo Finance]""" try: # Debug if cfg.DEBUG: # pylint: disable=logging-too-many-args logger.debug( "!stocks.ta.rsi %s %s %s %s %s %s", ticker, length, scalar, drift, start, end, ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) if not length.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") length = float(length) if not scalar.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") scalar = float(scalar) if not drift.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") drift = float(drift) ticker = ticker.upper() df_stock = discordbot.helpers.load(ticker, start) if df_stock.empty: raise Exception("Stock ticker is invalid") # Retrieve Data df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)] df_ta = momentum_model.rsi(df_stock["Adj Close"], length, scalar, drift) # Output Data fig, axes = plt.subplots(2, 1, figsize=plot_autoscale(), dpi=PLOT_DPI) ax = axes[0] ax.plot(df_stock.index, df_stock["Adj Close"].values, "k", lw=2) ax.set_title(f" {ticker} RSI{length} ") ax.set_xlim(df_stock.index[0], df_stock.index[-1]) ax.set_ylabel("Share Price ($)") ax.grid(b=True, which="major", color="#666666", linestyle="-") ax2 = axes[1] ax2.plot(df_ta.index, df_ta.values, "b", lw=2) ax2.set_xlim(df_stock.index[0], df_stock.index[-1]) ax2.axhspan(70, 100, facecolor="r", alpha=0.2) ax2.axhspan(0, 30, facecolor="g", alpha=0.2) ax2.axhline(70, linewidth=3, color="r", ls="--") ax2.axhline(30, linewidth=3, color="g", ls="--") ax2.grid(b=True, which="major", color="#666666", linestyle="-") ax2.set_ylim([0, 100]) ax3 = ax2.twinx() ax3.set_ylim(ax2.get_ylim()) ax3.set_yticks([30, 70]) ax3.set_yticklabels(["OVERSOLD", "OVERBOUGHT"]) plt.gcf().autofmt_xdate() fig.tight_layout(pad=1) plt.savefig("ta_rsi.png") uploaded_image = gst_imgur.upload_image("ta_rsi.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = "Stocks: Relative-Strength-Index " + ticker embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("ta_rsi.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stocks: Relative-Strength-Index", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def cg_command(ctx, ticker="", length="14", start="", end=""): """Displays chart with centre of gravity [Yahoo Finance]""" try: # Debug if cfg.DEBUG: logger.debug( "!stocks.ta.cg %s %s %s %s", ticker, length, start, end, ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) if not length.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") length = float(length) ticker = ticker.upper() df_stock = discordbot.helpers.load(ticker, start) if df_stock.empty: raise Exception("Stock ticker is invalid") # Retrieve Data df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)] df_close = df_stock["Adj Close"] df_close.columns = ["values"] df_ta = momentum_model.cg(df_close, length) # Output Data fig, axes = plt.subplots(2, 1, figsize=plot_autoscale(), dpi=PLOT_DPI) ax = axes[0] ax.set_title(f"{ticker} Centre of Gravity") ax.plot(df_stock.index, df_stock["Adj Close"].values, "k", lw=1) ax.set_xlim(df_stock.index[0], df_stock.index[-1]) ax.set_ylabel("Share Price ($)") ax.grid(b=True, which="major", color="#666666", linestyle="-") ax2 = axes[1] ax2.plot(df_ta.index, df_ta.values, "b", lw=2, label="CG") # shift cg 1 bar forward for signal signal = df_ta.values signal = np.roll(signal, 1) ax2.plot(df_ta.index, signal, "g", lw=1, label="Signal") ax2.set_xlim(df_stock.index[0], df_stock.index[-1]) ax2.grid(b=True, which="major", color="#666666", linestyle="-") plt.gcf().autofmt_xdate() fig.tight_layout(pad=1) plt.legend() plt.savefig("ta_cg.png") uploaded_image = gst_imgur.upload_image("ta_cg.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = "Stocks: Center-of-Gravity " + ticker embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("ta_cg.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stocks: Center-of-Gravity", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def pt_command(ctx, ticker="", raw="", start=""): """Displays price targets [Business Insider]""" try: # Debug if cfg.DEBUG: logger.debug("!stocks.dd.pt %s", ticker) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if raw in ["false", "False", "FALSE", ""]: raw = False if raw in ["true", "True", "TRUE"]: raw = True if raw not in [True, False]: raise Exception("raw argument has to be true or false") df_analyst_data = business_insider_model.get_price_target_from_analysts( ticker) stock = discordbot.helpers.load(ticker, start) if df_analyst_data.empty or stock.empty: raise Exception("Enter valid ticker") # Output Data if raw: df_analyst_data.sort_index(ascending=False) report = "´´´" + df_analyst_data.to_string() + "´´´" embed = discord.Embed( title="Stocks: [Business Insider] Price Targets", description=report, colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) ctx.send(embed=embed) else: plt.figure(dpi=PLOT_DPI) if start: df_analyst_data = df_analyst_data[start:] plt.plot(stock.index, stock["Adj Close"].values, lw=3) plt.plot(df_analyst_data.groupby(by=["Date"]).mean()) plt.scatter(df_analyst_data.index, df_analyst_data["Price Target"], c="r", s=40) plt.legend( ["Closing Price", "Average Price Target", "Price Target"]) plt.title(f"{ticker} (Time Series) and Price Target") plt.xlim(stock.index[0], stock.index[-1]) plt.xlabel("Time") plt.ylabel("Share Price") plt.grid(b=True, which="major", color="#666666", linestyle="-") plt.gcf().autofmt_xdate() plt.savefig("ta_pt.png") uploaded_image = gst_imgur.upload_image("ta_pt.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = "Stocks: [Business Insider] Price Targets " + ticker embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("ta_pt.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stocks: [Business Insider] Price Targets", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def contracts_command(ctx, ticker="", past_transaction_days="", raw=""): """Displays contracts associated with tickers [quiverquant.com]""" try: # Debug user input if cfg.DEBUG: logger.debug( "!stocks.gov.contracts %s %s %s", ticker, past_transaction_days, raw ) if past_transaction_days == "": past_transaction_days = 10 else: if not past_transaction_days.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") past_transaction_days = int(past_transaction_days) if raw in ["false", "False", "FALSE", ""]: raw = False if raw in ["true", "True", "TRUE"]: raw = True if raw not in [True, False]: raise Exception("raw argument has to be true or false") if ticker == "": raise Exception("A ticker is required") # Retrieve Data df_contracts = quiverquant_model.get_government_trading("contracts", ticker) if df_contracts.empty: raise Exception("No government contracts found") # Output Data df_contracts["Date"] = pd.to_datetime(df_contracts["Date"]).dt.date df_contracts = df_contracts[ df_contracts["Date"].isin( df_contracts["Date"].unique()[:past_transaction_days] ) ] df_contracts.drop_duplicates(inplace=True) fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) df_contracts.groupby("Date").sum().div(1000).plot(kind="bar", rot=0, ax=ax) ax.set_ylabel("Amount ($1k)") ax.set_title(f"Sum of latest government contracts to {ticker}") fig.tight_layout() plt.savefig("gov_contracts.png") uploaded_image = gst_imgur.upload_image("gov_contracts.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = f"Stocks: [quiverquant.com] Contracts by {ticker}" if raw: description = df_contracts.to_string() embed = discord.Embed( title=title, description=description, colour=cfg.COLOR ) else: embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("gov_contracts.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title=f"ERROR Stocks: [quiverquant.com] Contracts by {ticker}", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def aroon_command(ctx, ticker="", length="25", scalar="100", start="", end=""): """Displays chart with aroon indicator [Yahoo Finance]""" try: # Debug if cfg.DEBUG: logger.debug( "!stocks.ta.aroon %s %s %s %s %s", ticker, length, scalar, start, end, ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) if not length.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") length = int(length) if not scalar.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") scalar = float(scalar) ticker = ticker.upper() df_stock = discordbot.helpers.load(ticker, start) if df_stock.empty: raise Exception("Stock ticker is invalid") # Retrieve Data df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)] df_ta = trend_indicators_model.aroon(df_stock["High"], df_stock["Low"], length, scalar) fig, ax = plt.subplots(3, 1, figsize=plot_autoscale(), dpi=PLOT_DPI) ax0 = ax[0] ax0.plot(df_stock.index, df_stock["Adj Close"].values, "k", lw=2) ax0.set_title(f"Aroon on {ticker}") ax0.set_xlim(df_stock.index[0], df_stock.index[-1]) ax0.set_ylabel("Share Price ($)") ax0.grid(b=True, which="major", color="#666666", linestyle="-") ax1 = ax[1] ax1.plot(df_ta.index, df_ta.iloc[:, 0].values, "r", lw=2) ax1.plot(df_ta.index, df_ta.iloc[:, 1].values, "g", lw=2) ax1.set_xlim(df_stock.index[0], df_stock.index[-1]) ax1.axhline(50, linewidth=1, color="k", ls="--") ax1.legend( [ f"Aroon DOWN ({df_ta.columns[0]})", f"Aroon UP ({df_ta.columns[1]})" ], loc="upper left", ) ax1.grid(b=True, which="major", color="#666666", linestyle="-") ax1.set_ylim([0, 100]) ax2 = ax[2] ax2.plot(df_ta.index, df_ta.iloc[:, 2].values, "b", lw=2) ax2.set_xlim(df_stock.index[0], df_stock.index[-1]) ax2.set_xlabel("Time") ax2.legend([f"Aroon OSC ({df_ta.columns[2]})"], loc="upper left") ax2.grid(b=True, which="major", color="#666666", linestyle="-") ax2.set_ylim([-100, 100]) fig.tight_layout(pad=1) plt.gcf().autofmt_xdate() plt.savefig("ta_aroon.png") uploaded_image = gst_imgur.upload_image("ta_aroon.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = "Stocks: Aroon-Indicator " + ticker embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("ta_aroon.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stocks: Aroon-Indicator", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def fisher_command(ctx, ticker="", length="14", start="", end=""): """Displays chart with fisher transformation [Yahoo Finance]""" try: # Debug if cfg.DEBUG: logger.debug("!stocks.ta.fisher %s %s %s %s", ticker, length, start, end) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) if not length.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") length = int(length) ticker = ticker.upper() df_stock = discordbot.helpers.load(ticker, start) if df_stock.empty: raise Exception("Stock ticker is invalid") # Retrieve Data df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)] df_ta = momentum_model.fisher(df_stock["High"], df_stock["Low"], length) # Output Data fig, axes = plt.subplots(2, 1, figsize=plot_autoscale(), dpi=PLOT_DPI) ax = axes[0] ax.set_title(f"{ticker} Fisher Transform") ax.plot(df_stock.index, df_stock["Adj Close"].values, "k", lw=1) ax.set_xlim(df_stock.index[0], df_stock.index[-1]) ax.set_ylabel("Price") ax.grid(b=True, which="major", color="#666666", linestyle="-") ax2 = axes[1] ax2.plot( df_ta.index, df_ta.iloc[:, 0].values, "b", lw=2, label="Fisher", ) ax2.plot( df_ta.index, df_ta.iloc[:, 1].values, "fuchsia", lw=2, label="Signal", ) ax2.set_xlim(df_stock.index[0], df_stock.index[-1]) ax2.axhspan(2, plt.gca().get_ylim()[1], facecolor="r", alpha=0.2) ax2.axhspan(plt.gca().get_ylim()[0], -2, facecolor="g", alpha=0.2) ax2.axhline(2, linewidth=3, color="r", ls="--") ax2.axhline(-2, linewidth=3, color="g", ls="--") ax2.grid(b=True, which="major", color="#666666", linestyle="-") ax2.set_xlim(df_stock.index[0], df_stock.index[-1]) ax2.axhspan(2, plt.gca().get_ylim()[1], facecolor="r", alpha=0.2) ax2.axhspan(plt.gca().get_ylim()[0], -2, facecolor="g", alpha=0.2) ax2.axhline(2, linewidth=3, color="r", ls="--") ax2.axhline(-2, linewidth=3, color="g", ls="--") ax2.grid(b=True, which="major", color="#666666", linestyle="-") ax2.set_yticks([-2, 0, 2]) ax2.set_yticklabels(["-2 STDEV", "0", "+2 STDEV"]) plt.gcf().autofmt_xdate() fig.tight_layout(pad=1) plt.legend() plt.savefig("ta_fisher.png") uploaded_image = gst_imgur.upload_image("ta_fisher.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = "Stocks: Fisher-Transform " + ticker embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("ta_fisher.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stocks: Fisher-Transform", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def ftd_command(ctx, ticker="", start="", end=""): """Fails-to-deliver data [SEC]""" try: # Debug user input if cfg.DEBUG: logger.debug("!stocks.dps.ftd %s %s %s", ticker, start, end) # Check for argument if ticker == "": raise Exception("Stock ticker is required") ticker = ticker.upper() stock = yf.download(ticker, progress=False) if stock.empty: raise Exception("Stock ticker is invalid") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) # Retrieve data ftds_data = sec_model.get_fails_to_deliver(ticker, start, end, 0) # Debug user output if cfg.DEBUG: logger.debug(ftds_data.to_string()) plt.figure(dpi=PLOT_DPI) # Output data plt.bar( ftds_data["SETTLEMENT DATE"], ftds_data["QUANTITY (FAILS)"] / 1000, ) plt.ylabel("Shares [K]") plt.title(f"Fails-to-deliver Data for {ticker}") plt.grid(b=True, which="major", color="#666666", linestyle="-", alpha=0.2) plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%Y/%m/%d")) plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=7)) plt.gcf().autofmt_xdate() plt.xlabel("Days") _ = plt.gca().twinx() stock = discordbot.helpers.load(ticker, start) stock_ftd = stock[stock.index > start] stock_ftd = stock_ftd[stock_ftd.index < end] plt.plot(stock_ftd.index, stock_ftd["Adj Close"], color="tab:orange") plt.ylabel("Share Price [$]") plt.savefig("dps_ftd.png") plt.close("all") uploaded_image = gst_imgur.upload_image("dps_ftd.png", title="something") image_link = uploaded_image.link title = "Stocks: [SEC] Failure-to-deliver " + ticker embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("dps_ftd.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title=f"ERROR Stocks: [SEC] Failure-to-deliver {ticker}", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def technical_command(ctx, preset="template", sort="", limit="5", ascend="False"): """Displays stocks according to chosen preset, sorting by technical factors [Finviz]""" try: # Check for argument if preset == "template" or preset not in so.all_presets: raise Exception("Invalid preset selected!") # Debug if cfg.DEBUG: logger.debug( "!stocks.scr.technical %s %s %s %s", preset, sort, limit, ascend ) # Check for argument if not limit.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") limit = int(limit) if limit < 0: raise Exception("Number has to be above 0") if ascend.lower() == "false": ascend = False elif ascend.lower() == "true": ascend = True else: raise Exception("ascend argument has to be true or false") # Output Data df_screen = get_screener_data( preset, "technical", limit, ascend, ) description = "" if isinstance(df_screen, pd.DataFrame): if df_screen.empty: return [] df_screen = df_screen.dropna(axis="columns", how="all") if sort: if " ".join(sort) in so.d_cols_to_sort["technical"]: df_screen = df_screen.sort_values( by=[" ".join(sort)], ascending=ascend, na_position="last", ) else: similar_cmd = difflib.get_close_matches( " ".join(sort), so.d_cols_to_sort["technical"], n=1, cutoff=0.7, ) if similar_cmd: description = f"Replacing '{' '.join(sort)}' by '{similar_cmd[0]}' so table can be sorted.\n\n" df_screen = df_screen.sort_values( by=[similar_cmd[0]], ascending=ascend, na_position="last", ) else: raise ValueError( f"Wrong sort column provided! Select from: {', '.join(so.d_cols_to_sort['technical'])}" ) df_screen = df_screen.fillna("") future_column_name = df_screen["Ticker"] df_screen = df_screen.head(n=limit).transpose() df_screen.columns = future_column_name df_screen.drop("Ticker") columns = [] initial_str = description + "Page 0: Overview" i = 1 for column in df_screen.columns.values: initial_str = initial_str + "\nPage " + str(i) + ": " + column i += 1 columns.append( discord.Embed( title="Stocks: [Finviz] Technical Screener", description=initial_str, colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) ) for column in df_screen.columns.values: columns.append( discord.Embed( title="Stocks: [Finviz] Technical Screener", description="```" + df_screen[column].fillna("").to_string() + "```", colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) ) await pagination(columns, ctx) except Exception as e: embed = discord.Embed( title="ERROR Stocks: [Finviz] Technical Screener", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def macd_command(ctx, ticker="", fast="12", slow="26", signal="9", start="", end=""): """Displays chart with moving average convergence/divergence [Yahoo Finance]""" try: # Debug if cfg.DEBUG: # pylint: disable=logging-too-many-args logger.debug( "!stocks.ta.macd %s %s %s %s %s %s", ticker, fast, slow, signal, start, end, ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) if not fast.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") fast = float(fast) if not slow.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") slow = float(slow) if not signal.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") signal = float(signal) ticker = ticker.upper() df_stock = discordbot.helpers.load(ticker, start) if df_stock.empty: raise Exception("Stock ticker is invalid") # Retrieve Data df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)] df_ta = momentum_model.macd(df_stock["Adj Close"], fast, slow, signal) # Output Data fig, axes = plt.subplots(2, 1, figsize=plot_autoscale(), dpi=PLOT_DPI) ax = axes[0] ax.set_title(f"{ticker} MACD") ax.plot(df_stock.index, df_stock["Adj Close"].values, "k", lw=2) ax.set_xlim(df_stock.index[0], df_stock.index[-1]) ax.set_ylabel("Share Price ($)") ax.grid(b=True, which="major", color="#666666", linestyle="-") ax2 = axes[1] ax2.plot(df_ta.index, df_ta.iloc[:, 0].values, "b", lw=2) ax2.plot(df_ta.index, df_ta.iloc[:, 2].values, "r", lw=2) ax2.bar(df_ta.index, df_ta.iloc[:, 1].values, color="g") ax2.legend( [ f"MACD Line {df_ta.columns[0]}", f"Signal Line {df_ta.columns[2]}", f"Histogram {df_ta.columns[1]}", ], loc="upper left", ) ax2.set_xlim(df_stock.index[0], df_stock.index[-1]) ax2.grid(b=True, which="major", color="#666666", linestyle="-") plt.gcf().autofmt_xdate() fig.tight_layout(pad=1) plt.savefig("ta_macd.png") uploaded_image = gst_imgur.upload_image("ta_macd.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = "Stocks: Moving-Average-Convergence-Divergence " + ticker embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("ta_macd.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stocks: Moving-Average-Convergence-Divergence", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def wma_command(ctx, ticker="", window="", offset="", start="", end=""): """Displays chart with weighted moving average [Yahoo Finance]""" try: # Debug if cfg.DEBUG: logger.debug( "!stocks.ta.wma %s %s %s %s %s", ticker, window, offset, start, end, ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) l_legend = [ticker] if window == "": window = [20, 50] else: window_temp = list() for wind in window.split(","): try: window_temp.append(float(wind)) except Exception as e: raise Exception("Window needs to be a float") from e window = window_temp if offset == "": offset = 0 else: if not offset.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") offset = float(offset) ticker = ticker.upper() stock = discordbot.helpers.load(ticker, start) if stock.empty: raise Exception("Stock ticker is invalid") # Retrieve Data price_df = pd.DataFrame(stock["Adj Close"].values, columns=["Price"], index=stock.index) i = 1 for win in window: wma_data = overlap_model.wma(values=stock["Adj Close"], length=win, offset=offset) price_df = price_df.join(wma_data) l_legend.append(f"WMA {win}") i += 1 # Output Data start = start.strftime("%Y-%m-%d") end = end.strftime("%Y-%m-%d") price_df = price_df.loc[(price_df.index >= start) & (price_df.index < end)] fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) ax.set_title(f"{ticker} WMA") ax.plot(price_df.index, price_df["Price"], lw=3, c="k") ax.set_xlabel("Time") ax.set_xlim([price_df.index[0], price_df.index[-1]]) ax.set_ylabel(f"{ticker} Price") for idx in range(1, price_df.shape[1]): ax.plot(price_df.iloc[:, idx]) ax.legend(l_legend) ax.grid(b=True, which="major", color="#666666", linestyle="-") plt.gcf().autofmt_xdate() fig.tight_layout(pad=1) plt.savefig("ta_wma.png") uploaded_image = gst_imgur.upload_image("ta_wma.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = "Stocks: Weighted-Moving-Average " + ticker embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("ta_wma.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stocks: Weighted-Moving-Average", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def gtrades_command(ctx, ticker="", gov_type="", past_transactions_months="", raw=""): """Displays government trades [quiverquant.com]""" try: # Debug user input if cfg.DEBUG: logger.debug( "!stocks.gov.gtrades %s %s %s %s", ticker, gov_type, past_transactions_months, raw, ) if past_transactions_months == "": past_transactions_months = 10 else: if not past_transactions_months.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") past_transactions_months = float(past_transactions_months) if raw in ["false", "False", "FALSE", ""]: raw = False if raw in ["true", "True", "TRUE"]: raw = True if raw not in [True, False]: raise Exception("raw argument has to be true or false") if ticker == "": raise Exception("A ticker is required") possible_args = ["congress", "senate", "house"] if gov_type == "": gov_type = "congress" elif gov_type not in possible_args: raise Exception( "Enter a valid government argument, options are: congress, senate and house" ) # Retrieve Data df_gov = quiverquant_model.get_government_trading(gov_type, ticker) if df_gov.empty: raise Exception(f"No {gov_type} trading data found") # Output Data df_gov = df_gov.sort_values("TransactionDate", ascending=False) start_date = datetime.now() - timedelta(days=past_transactions_months * 30) df_gov["TransactionDate"] = pd.to_datetime(df_gov["TransactionDate"]) df_gov = df_gov[df_gov["TransactionDate"] > start_date] if df_gov.empty: logger.debug("No recent %s trading data found", gov_type) return df_gov["min"] = df_gov["Range"].apply( lambda x: x.split("-")[0].strip("$").replace(",", "").strip()) df_gov["max"] = df_gov["Range"].apply( lambda x: x.split("-")[1].replace(",", "").strip().strip("$") if "-" in x else x.strip("$").replace(",", "").split("\n")[0]) df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply( lambda x: int(float(x["min"])) if x["Transaction"] == "Purchase" else -int(float(x["max"])), axis=1, ) df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply( lambda x: int(float(x["max"])) if x["Transaction"] == "Purchase" else -1 * int(float(x["min"])), axis=1, ) df_gov = df_gov.sort_values("TransactionDate", ascending=True) fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) ax.fill_between( df_gov["TransactionDate"].unique(), df_gov.groupby("TransactionDate")["lower"].sum().values / 1000, df_gov.groupby("TransactionDate")["upper"].sum().values / 1000, ) ax.set_xlim([ df_gov["TransactionDate"].values[0], df_gov["TransactionDate"].values[-1], ]) ax.grid() ax.set_title(f"{gov_type.capitalize()} trading on {ticker}") ax.set_xlabel("Date") ax.set_ylabel("Amount ($1k)") plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%Y/%m/%d")) plt.gcf().autofmt_xdate() fig.tight_layout() plt.savefig("gov_gtrades.png") uploaded_image = gst_imgur.upload_image("gov_gtrades.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = "Stocks: [quiverquant.com] Government Trades" if raw: description = df_gov.to_string() embed = discord.Embed(title=title, description=description, colour=cfg.COLOR) else: embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("gov_gtrades.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stocks: [quiverquant.com] Government Trades", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def sidtc_command(ctx, sort="float", num="10"): """Short interest and days to cover [Stockgrid]""" try: # Debug if cfg.DEBUG: logger.debug("!stocks.dps.sidtc %s %s", sort, num) # Check for argument possible_sorts = ("float", "dtc", "si") if sort not in possible_sorts: raise Exception( f"The possible sorts are: {', '.join(possible_sorts)}") if not num.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") num = int(num) if num < 0: raise Exception("Number has to be above 0") # Retrieve data df = stockgrid_model.get_short_interest_days_to_cover(sort) df = df.iloc[:num] # Debug user output if cfg.DEBUG: logger.debug(df.to_string()) # Output data dp_date = df["Date"].values[0] df = df.drop(columns=["Date"]) df["Short Interest"] = df["Short Interest"] / 1_000_000 df.head() df.columns = [ "Ticker", "Float Short %", "Days to Cover", "Short Interest (1M)", ] future_column_name = df["Ticker"] df = df.transpose() df.columns = future_column_name df.drop("Ticker") columns = [] initial_str = "Page 0: Overview" i = 1 for column in df.columns.values: initial_str = initial_str + "\nPage " + str(i) + ": " + column i += 1 columns.append( discord.Embed(title="Dark Pool Shorts", description=initial_str, colour=cfg.COLOR).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) for column in df.columns.values: columns.append( discord.Embed( title= "Stocks: [Stockgrid] Short Interest and Days to Cover", description= "```The following data corresponds to the date: " + dp_date + "\n\n" + df[column].fillna("").to_string() + "```", colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) await pagination(columns, ctx) except Exception as e: embed = discord.Embed( title="ERROR Stocks: [Stockgrid] Short Interest and Days to Cover", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def calls_command(ctx, ticker: str = "", expiration_date: str = ""): """Show calls for given ticker and expiration""" try: # Debug if cfg.DEBUG: logger.debug("!stocks.opt.calls %s %s", ticker, expiration_date) # Check for argument if ticker == "": raise Exception("Stock ticker is required") calls = yf.Ticker(ticker).option_chain(expiration_date).calls.fillna(0) calls = calls[["strike", "bid", "ask", "volume", "openInterest"]] calls_string = tabulate( calls, headers=calls.columns, showindex=False, tablefmt="pipe", numalign="left", stralign="center", ) if len(calls_string) <= 4000: embed = discord.Embed( title= f"Stocks: Call Options [yfinance] for {ticker} on {expiration_date}", description="```" + calls_string + "```", colour=cfg.COLOR, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed) else: i = 0 str_start = 0 str_end = 4000 columns = [] while i <= len(calls_string) / 4000: columns.append( discord.Embed( title= f"Stocks: Call Options [yfinance] for {ticker} on {expiration_date}", description="```" + calls_string[str_start:str_end] + "```", colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) str_end = str_start str_start += 4000 i += 1 await pagination(columns, ctx) await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stock-Options: Expirations", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def qtrcontracts_command(ctx, num="", analysis=""): """Displays a look at government contracts [quiverquant.com]""" try: # Debug user input if cfg.DEBUG: logger.debug("!stocks.gov.qtrcontracts %s %s", num, analysis) if num == "": num = 20 else: if not num.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") num = int(num) possible_args = ["total", "upmom", "downmom"] if analysis == "": analysis = "total" elif analysis not in possible_args: raise Exception( "Enter a valid analysis argument, options are: total, upmom and downmom" ) # Retrieve Data df_contracts = quiverquant_model.get_government_trading( "quarter-contracts") if df_contracts.empty: raise Exception("No quarterly government contracts found") tickers = quiverquant_model.analyze_qtr_contracts(analysis, num) # Output Data if analysis in {"upmom", "downmom"}: description = tickers.to_string() fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) max_amount = 0 quarter_ticks = [] for symbol in tickers: amounts = ( df_contracts[df_contracts["Ticker"] == symbol].sort_values( by=["Year", "Qtr"])["Amount"].values) qtr = ( df_contracts[df_contracts["Ticker"] == symbol].sort_values( by=["Year", "Qtr"])["Qtr"].values) year = ( df_contracts[df_contracts["Ticker"] == symbol].sort_values( by=["Year", "Qtr"])["Year"].values) ax.plot(np.arange(0, len(amounts)), amounts / 1_000_000, "-*", lw=2, ms=15) if len(amounts) > max_amount: max_amount = len(amounts) quarter_ticks = [ f"{quarter[0]} - Q{quarter[1]} " for quarter in zip(year, qtr) ] ax.set_xlim([-0.5, max_amount - 0.5]) ax.set_xticks(np.arange(0, max_amount)) ax.set_xticklabels(quarter_ticks) ax.grid() ax.legend(tickers) titles = { "upmom": "Highest increasing quarterly Government Contracts", "downmom": "Highest decreasing quarterly Government Contracts", } ax.set_title(titles[analysis]) ax.set_xlabel("Date") ax.set_ylabel("Amount ($1M)") fig.tight_layout() plt.savefig("gov_qtrcontracts.png") uploaded_image = gst_imgur.upload_image("gov_qtrcontracts.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = "Stocks: [quiverquant.com] Government contracts" embed = discord.Embed(title=title, description=description, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("gov_qtrcontracts.png") await ctx.send(embed=embed) elif analysis == "total": tickers.index = [ ind + " " * (7 - len(ind)) for ind in tickers.index ] tickers[:] = [ str(round(val[0] / 1e9, 2)) for val in tickers.values ] tickers.columns = ["Amount [M]"] tickers_str = tabulate( tickers, headers=tickers.columns, showindex=True, numalign="right", stralign="center", ) embed = discord.Embed( title="Stocks: [quiverquant.com] Government contracts", description=tickers_str, colour=cfg.COLOR, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stocks: [quiverquant.com] Government contracts", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def histcont_command(ctx, ticker=""): """Displays historical quarterly-contracts [quiverquant.com]""" try: # Debug user input if cfg.DEBUG: logger.debug("!stocks.gov.histcont %s", ticker) if ticker == "": raise Exception("A ticker is required") # Retrieve Data df_contracts = quiverquant_model.get_government_trading( "quarter-contracts", ticker=ticker) if df_contracts.empty: logger.debug("No quarterly government contracts found") return # Output Data amounts = df_contracts.sort_values(by=["Year", "Qtr"])["Amount"].values qtr = df_contracts.sort_values(by=["Year", "Qtr"])["Qtr"].values year = df_contracts.sort_values(by=["Year", "Qtr"])["Year"].values quarter_ticks = [ f"{quarter[0]}" if quarter[1] == 1 else "" for quarter in zip(year, qtr) ] fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) ax.plot(np.arange(0, len(amounts)), amounts / 1000, "-*", lw=2, ms=15) ax.set_xlim([-0.5, len(amounts) - 0.5]) ax.set_xticks(np.arange(0, len(amounts))) ax.set_xticklabels(quarter_ticks) ax.grid() ax.set_title( f"Historical Quarterly Government Contracts for {ticker.upper()}") ax.set_xlabel("Date") ax.set_ylabel("Amount ($1k)") fig.tight_layout() plt.savefig("gov_histcont.png") uploaded_image = gst_imgur.upload_image("gov_histcont.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = "Stocks: Historical Quarterly Government Contract " + ticker embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("gov_histcont.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stocks: Historical Quarterly Government Contract", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def unu_command(ctx, num: int = None): """Unusual Options""" try: # Debug if cfg.DEBUG: logger.debug("!stocks.opt.unu %s", num) # Check for argument if num is None: num = 10 pages = np.arange(0, num // 20 + 1) data_list = [] for page_num in pages: r = requests.get( f"https://app.fdscanner.com/api2/unusualvolume?p=0&page_size=20&page={int(page_num)}", headers={"User-Agent": get_user_agent()}, ) if r.status_code != 200: logger.debug("Error in fdscanner request") return pd.DataFrame(), "request error" data_list.append(r.json()) ticker, expiry, option_strike, option_type, ask, bid, oi, vol, voi = ( [], [], [], [], [], [], [], [], [], ) for data in data_list: for entry in data["data"]: ticker.append(entry["tk"]) expiry.append(entry["expiry"]) option_strike.append(float(entry["s"])) option_type.append("Put" if entry["t"] == "P" else "Call") ask.append(entry["a"]) bid.append(entry["b"]) oi.append(entry["oi"]) vol.append(entry["v"]) voi.append(entry["vol/oi"]) df = pd.DataFrame( { "Ticker": ticker, "Exp": expiry, "Strike": option_strike, "Type": option_type, "Vol/OI": voi, "Vol": vol, "OI": oi, } ) df = df.replace({"2021-", "2022-"}, "", regex=True) report = ( "```" + tabulate( df, headers=["T", "Exp", "ST", "C/P", "V/O", "Vol", "OI"], tablefmt="fancy_grid", showindex=False, floatfmt=["", "", ".1f", "", ".1f", ".0f", ".0f", ".2f", ".2f"], ) + "```" ) embed = discord.Embed( title="Unusual Options", description=report, colour=cfg.COLOR, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Unusual Options", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def stoch_command(ctx, ticker="", fast_k="14", slow_d="3", slow_k="3", start="", end=""): """Displays chart with stochastic relative strength average [Yahoo Finance]""" try: # Debug if cfg.DEBUG: logger.debug( "!stocks.ta.stoch %s %s %s %s %s %s", ticker, fast_k, slow_k, slow_d, start, end, ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) if not fast_k.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") fast_k = int(fast_k) if not slow_k.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") slow_k = int(slow_k) if not slow_d.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") slow_d = int(slow_d) ticker = ticker.upper() df_stock = discordbot.helpers.load(ticker, start) if df_stock.empty: raise Exception("Stock ticker is invalid") # Retrieve Data df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)] df_ta = momentum_model.stoch( df_stock["High"], df_stock["Low"], df_stock["Adj Close"], fast_k, slow_d, slow_k, ) # Output Data fig, axes = plt.subplots(2, 1, figsize=plot_autoscale(), dpi=PLOT_DPI) ax = axes[0] ax.plot(df_stock.index, df_stock["Adj Close"].values, "k", lw=2) ax.set_title( f"Stochastic Relative Strength Index (STOCH RSI) on {ticker}") ax.set_xlim(df_stock.index[0], df_stock.index[-1]) ax.set_xticklabels([]) ax.set_ylabel("Share Price ($)") ax.grid(b=True, which="major", color="#666666", linestyle="-") ax2 = axes[1] ax2.plot(df_ta.index, df_ta.iloc[:, 0].values, "k", lw=2) ax2.plot(df_ta.index, df_ta.iloc[:, 1].values, "b", lw=2, ls="--") ax2.set_xlim(df_stock.index[0], df_stock.index[-1]) ax2.axhspan(80, 100, facecolor="r", alpha=0.2) ax2.axhspan(0, 20, facecolor="g", alpha=0.2) ax2.axhline(80, linewidth=3, color="r", ls="--") ax2.axhline(20, linewidth=3, color="g", ls="--") ax2.grid(b=True, which="major", color="#666666", linestyle="-") ax2.set_ylim([0, 100]) ax3 = ax2.twinx() ax3.set_ylim(ax2.get_ylim()) ax3.set_yticks([20, 80]) ax3.set_yticklabels(["OVERSOLD", "OVERBOUGHT"]) ax2.legend([f"%K {df_ta.columns[0]}", f"%D {df_ta.columns[1]}"], loc="lower left") plt.gcf().autofmt_xdate() fig.tight_layout(pad=1) plt.savefig("ta_stoch.png") uploaded_image = gst_imgur.upload_image("ta_stoch.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = "Stocks: Stochastic-Relative-Strength-Index " + ticker embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("ta_stoch.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stocks: Stochastic-Relative-Strength-Index", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def lastcontracts_command(ctx, past_transactions_days="", num=""): """Displays last government contracts [quiverquant.com]""" try: # Debug user input if cfg.DEBUG: logger.debug("!stocks.gov.lastcontracts %s %s", past_transactions_days, num) if past_transactions_days == "": past_transactions_days = 2 else: if not past_transactions_days.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") past_transactions_days = float(past_transactions_days) if num == "": num = 20 else: if not num.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") num = int(num) df_contracts = quiverquant_model.get_government_trading("contracts") if df_contracts.empty: logger.debug("No government contracts found") return df_contracts.sort_values("Date", ascending=False) df_contracts["Date"] = pd.to_datetime(df_contracts["Date"]) df_contracts["Date"] = df_contracts["Date"].dt.date df_contracts.drop_duplicates(inplace=True) df_contracts = df_contracts[df_contracts["Date"].isin( df_contracts["Date"].unique()[:past_transactions_days])] df_contracts = df_contracts[["Date", "Ticker", "Amount", "Agency"]][:num] initial_str = "Page 0: Overview" i = 1 for col_name in df_contracts["Ticker"].values: initial_str += f"\nPage {i}: {col_name}" i += 1 columns = [] df_contracts = df_contracts.T columns.append( discord.Embed( title="Stocks: [quiverquant.com] Top buy government trading", description=initial_str, colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) for column in df_contracts.columns.values: columns.append( discord.Embed( description="```" + df_contracts[column].fillna("").to_string() + "```", colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) await pagination(columns, ctx) except Exception as e: embed = discord.Embed( title="ERROR Stocks: [quiverquant.com] Top buy government trading", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def spos_command(ctx, ticker=""): """Net short vs position [Stockgrid]""" try: # Debug user input if cfg.DEBUG: logger.debug("!stocks.dps.spos %s", ticker) # Check for argument if ticker == "": raise Exception("Stock ticker is required") ticker = ticker.upper() stock = yf.download(ticker, progress=False) if stock.empty: raise Exception("Stock ticker is invalid") # Retrieve data df = stockgrid_model.get_net_short_position(ticker) # Debug user output if cfg.DEBUG: logger.debug(df.to_string()) # Output data title = f"Stocks: [Stockgrid] Net Short vs Position {ticker}" embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) fig = plt.figure(dpi=PLOT_DPI) ax = fig.add_subplot(111) ax.bar( df["dates"], df["dollar_net_volume"] / 1_000, color="r", alpha=0.4, label="Net Short Vol. (1k $)", ) ax.set_ylabel("Net Short Vol. (1k $)") ax2 = ax.twinx() ax2.plot( df["dates"].values, df["dollar_dp_position"], c="tab:blue", label="Position (1M $)", ) ax2.set_ylabel("Position (1M $)") lines, labels = ax.get_legend_handles_labels() lines2, labels2 = ax2.get_legend_handles_labels() ax2.legend(lines + lines2, labels + labels2, loc="upper left") ax.grid() plt.title(f"Net Short Vol. vs Position for {ticker}") plt.gcf().autofmt_xdate() file_name = ticker + "_spos.png" plt.savefig(file_name) plt.close("all") uploaded_image = gst_imgur.upload_image(file_name, title="something") image_link = uploaded_image.link embed.set_image(url=image_link) os.remove(file_name) await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title=f"ERROR Stocks: [Stockgrid] Net Short vs Position {ticker}", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def historical_command(ctx, signal="", start=""): """Displays historical price comparison between similar companies [Yahoo Finance]""" try: # Debug user input if cfg.DEBUG: logger.debug("!stocks.scr.historical %s %s", signal, start) # Check for argument if signal == "" or signal not in list(so.d_signals_desc.keys): raise Exception("Invalid preset selected!") register_matplotlib_converters() screen = ticker.Ticker() if signal in finviz_model.d_signals: screen.set_filter(signal=finviz_model.d_signals[signal]) else: preset_filter = configparser.RawConfigParser() preset_filter.optionxform = str # type: ignore preset_filter.read(so.presets_path + signal + ".ini") d_general = preset_filter["General"] d_filters = { **preset_filter["Descriptive"], **preset_filter["Fundamental"], **preset_filter["Technical"], } d_filters = {k: v for k, v in d_filters.items() if v} if d_general["Signal"]: screen.set_filter(filters_dict=d_filters, signal=d_general["Signal"]) else: screen.set_filter(filters_dict=d_filters) if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) # Output Data l_min = [] l_leg = [] l_stocks = screen.ScreenerView(verbose=0) if len(l_stocks) > 10: description = ( "\nThe limit of stocks to compare with are 10. Hence, 10 random similar stocks will be displayed." "\nThe selected list will be: ") random.shuffle(l_stocks) l_stocks = sorted(l_stocks[:10]) description = description + (", ".join(l_stocks)) logger.debug(description) plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI) while l_stocks: l_parsed_stocks = [] for symbol in l_stocks: try: df_similar_stock = yf.download( symbol, start=datetime.strftime(start, "%Y-%m-%d"), progress=False, threads=False, ) if not df_similar_stock.empty: plt.plot( df_similar_stock.index, df_similar_stock["Adj Close"].values, ) l_min.append(df_similar_stock.index[0]) l_leg.append(symbol) l_parsed_stocks.append(symbol) except Exception as e: error = ( f"{e}\nDisregard previous error, which is due to API Rate limits from Yahoo Finance. " f"Because we like '{symbol}', and we won't leave without getting data from it." ) embed = discord.Embed( title= "ERROR Stocks: [Yahoo Finance] Historical Screener", colour=cfg.COLOR, description=error, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed) for parsed_stock in l_parsed_stocks: l_stocks.remove(parsed_stock) if signal: plt.title( f"Screener Historical Price using {finviz_model.d_signals[signal]} signal" ) else: plt.title(f"Screener Historical Price using {signal} preset") plt.xlabel("Time") plt.ylabel("Share Price ($)") plt.legend(l_leg) plt.grid(b=True, which="major", color="#666666", linestyle="-") plt.minorticks_on() plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) # ensures that the historical data starts from same datapoint plt.xlim([max(l_min), df_similar_stock.index[-1]]) plt.savefig("scr_historical.png") uploaded_image = gst_imgur.upload_image("scr_historical.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = "Stocks: [Yahoo Finance] Historical Screener" embed = discord.Embed(title=title, description=description, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("scr_historical.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title="ERROR Stocks: [Yahoo Finance] Historical Screener", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def topbuys_command(ctx, gov_type="", past_transactions_months="", num="", raw=""): """Displays most purchased stocks by the congress/senate/house [quiverquant.com]""" try: # Debug user input if cfg.DEBUG: logger.debug( "!stocks.gov.topbuys %s %s %s %s", gov_type, past_transactions_months, num, raw, ) if past_transactions_months == "": past_transactions_months = 5 else: if not past_transactions_months.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") past_transactions_months = int(past_transactions_months) if num == "": num = 10 else: if not num.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") num = int(num) if raw in ["false", "False", "FALSE", ""]: raw = False if raw in ["true", "True", "TRUE"]: raw = True if raw not in [True, False]: raise Exception("raw argument has to be true or false") possible_args = ["congress", "senate", "house"] if gov_type == "": gov_type = "congress" elif gov_type not in possible_args: raise Exception( "Enter a valid government argument, options are: congress, senate and house" ) # Retrieve Data df_gov = quiverquant_model.get_government_trading(gov_type) if df_gov.empty: logger.debug("No %s trading data found", gov_type) return df_gov = df_gov.sort_values("TransactionDate", ascending=False) start_date = datetime.now() - timedelta(days=past_transactions_months * 30) df_gov["TransactionDate"] = pd.to_datetime(df_gov["TransactionDate"]) df_gov = df_gov[df_gov["TransactionDate"] > start_date].dropna() # Catch bug where error shown for purchase of >5,000,000 df_gov["Range"] = df_gov["Range"].apply( lambda x: "$5,000,001-$5,000,001" if x == ">$5,000,000" else x) df_gov["min"] = df_gov["Range"].apply( lambda x: x.split("-")[0].strip("$").replace(",", "").strip()) df_gov["max"] = df_gov["Range"].apply( lambda x: x.split("-")[1].replace(",", "").strip().strip("$") if "-" in x else x.strip("$").replace(",", "")) df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply( lambda x: float(x["min"]) if x["Transaction"] == "Purchase" else -float(x["max"]), axis=1, ) df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply( lambda x: float(x["max"]) if x["Transaction"] == "Purchase" else -float(x["min"]), axis=1, ) df_gov = df_gov.sort_values("TransactionDate", ascending=True) if raw: df = pd.DataFrame( df_gov.groupby("Ticker")["upper"].sum().div(1000).sort_values( ascending=False).head(n=num)) description = "```" + df.to_string() + "```" fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) df_gov.groupby("Ticker")["upper"].sum().div(1000).sort_values( ascending=False).head(n=num).plot(kind="bar", rot=0, ax=ax) ax.set_ylabel("Amount [1k $]") ax.set_title( f"Top {num} purchased stocks over last {past_transactions_months} " f"months (upper bound) for {gov_type.upper()}") plt.gcf().axes[0].yaxis.get_major_formatter().set_scientific(False) fig.tight_layout() plt.savefig("gov_topbuys.png") uploaded_image = gst_imgur.upload_image("gov_topbuys.png", title="something") image_link = uploaded_image.link if cfg.DEBUG: logger.debug("Image URL: %s", image_link) title = f"Stocks: [quiverquant.com] Top purchases for {gov_type.upper()}" if raw: embed = discord.Embed(title=title, description=description, colour=cfg.COLOR) else: embed = discord.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) embed.set_image(url=image_link) os.remove("gov_topbuys.png") await ctx.send(embed=embed) except Exception as e: embed = discord.Embed( title= f"ERROR Stocks: [quiverquant.com] Top purchases for {gov_type.upper()}", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def pos_command(ctx, sort="dpp_dollar", num="10"): """Dark pool short position [Stockgrid]""" try: # Debug user input if cfg.DEBUG: logger.debug("!stocks.dps.pos %s %s", sort, num) # Check for argument possible_sorts = ("sv", "sv_pct", "nsv", "nsv_dollar", "dpp", "dpp_dollar") if sort not in possible_sorts: raise Exception(f"The possible sorts are: {', '.join(possible_sorts)}") if not num.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") num = int(num) if num < 0: raise Exception("Number has to be above 0") # Retrieve data df = stockgrid_model.get_dark_pool_short_positions(sort, False) df = df.iloc[:num] # Debug user output if cfg.DEBUG: logger.debug(df.to_string()) # Output data dp_date = df["Date"].values[0] df = df.drop(columns=["Date"]) df["Net Short Volume $"] = df["Net Short Volume $"] / 100_000_000 df["Short Volume"] = df["Short Volume"] / 1_000_000 df["Net Short Volume"] = df["Net Short Volume"] / 1_000_000 df["Short Volume %"] = df["Short Volume %"] * 100 df["Dark Pools Position $"] = df["Dark Pools Position $"] / (1_000_000_000) df["Dark Pools Position"] = df["Dark Pools Position"] / 1_000_000 df.columns = [ "Ticker", "Short Vol. (1M)", "Short Vol. %", "Net Short Vol. (1M)", "Net Short Vol. ($100M)", "DP Position (1M)", "DP Position ($1B)", ] future_column_name = df["Ticker"] df = df.transpose() df.columns = future_column_name df.drop("Ticker") columns = [] initial_str = "Page 0: Overview" i = 1 for column in df.columns.values: initial_str = initial_str + "\nPage " + str(i) + ": " + column i += 1 columns.append( discord.Embed( title="Stocks: [Stockgrid] Dark Pool Short Position", description=initial_str, colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) ) for column in df.columns.values: columns.append( discord.Embed( title="High Short Interest", description="```The following data corresponds to the date: " + dp_date + "\n\n" + df[column].fillna("").to_string() + "```", colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) ) await pagination(columns, ctx) except Exception as e: embed = discord.Embed( title="ERROR Stocks: [Stockgrid] Dark Pool Short Position", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)
async def lasttrades_command(ctx, gov_type="", past_transactions_days="", representative=""): """Displays trades made by the congress/senate/house [quiverquant.com]""" try: # Debug user input if cfg.DEBUG: logger.debug( "!stocks.gov.lasttrades %s %s %s", gov_type, past_transactions_days, representative, ) if past_transactions_days == "": past_days = 5 else: if not past_transactions_days.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") past_days = int(past_transactions_days) possible_args = ["congress", "senate", "house"] if gov_type == "": gov_type = "congress" elif gov_type not in possible_args: raise Exception( "Enter a valid government argument, options are: congress, senate and house" ) # Retrieve Data df_gov = quiverquant_model.get_government_trading(gov_type) # Output Data if df_gov.empty: raise Exception(f"No {gov_type} trading data found") df_gov = df_gov.sort_values("TransactionDate", ascending=False) df_gov = df_gov[df_gov["TransactionDate"].isin( df_gov["TransactionDate"].unique()[:past_days])] if gov_type == "congress": df_gov = df_gov[[ "TransactionDate", "Ticker", "Representative", "Transaction", "Range", "House", "ReportDate", ]].rename( columns={ "TransactionDate": "Transaction Date", "ReportDate": "Report Date", }) else: df_gov = df_gov[[ "TransactionDate", "Ticker", "Representative", "Transaction", "Range", ]].rename(columns={"TransactionDate": "Transaction Date"}) if representative: df_gov_rep = df_gov[df_gov["Representative"].str.split().str[0] == representative] if df_gov_rep.empty: raise Exception( f"No representative {representative} found in the past {past_days}" f" days. The following are available: " f"{', '.join(df_gov['Representative'].str.split().str[0].unique())}" ) initial_str = "Page 0: Overview" i = 1 for col_name in df_gov_rep["Ticker"].values: initial_str += f"\nPage {i}: {col_name}" i += 1 columns = [] df_gov_rep = df_gov_rep.T columns.append( discord.Embed( title= f"Stocks: [quiverquant.com] Trades by {representative}", description=initial_str, colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) for column in df_gov_rep.columns.values: columns.append( discord.Embed( description="```" + df_gov_rep[column].fillna("").to_string() + "```", colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) await pagination(columns, ctx) else: initial_str = "Page 0: Overview" i = 1 for col_name in df_gov["Ticker"].values: initial_str += f"\nPage {i}: {col_name}" i += 1 columns = [] df_gov = df_gov.T columns.append( discord.Embed( title= f"Stocks: [quiverquant.com] Trades for {gov_type.upper()}", description=initial_str, colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) for column in df_gov.columns.values: columns.append( discord.Embed( description="```" + df_gov[column].fillna("").to_string() + "```", colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) await pagination(columns, ctx) except Exception as e: embed = discord.Embed( title= f"ERROR Stocks: [quiverquant.com] Trades by {gov_type.upper()}", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed)