From c528419aad0ebf3bdf870272eaa09ae5f5ca2431 Mon Sep 17 00:00:00 2001 From: northern-64bit <75195383+northern-64bit@users.noreply.github.com> Date: Fri, 19 Nov 2021 21:21:47 +0100 Subject: [PATCH] Discord Bot: Adding Government Commands (#956) * Discord Bot: Adding Government Commands * Fix linting * More linting * Even more linting * Adding topbuys and fixing the errors * Fixing errors and improving commands --- discordbot/discordbot.py | 1 + discordbot/stocks/government/contracts.py | 94 +++++ discordbot/stocks/government/gov_menu.py | 327 ++++++++++++++++++ discordbot/stocks/government/gtrades.py | 149 ++++++++ discordbot/stocks/government/histcont.py | 81 +++++ discordbot/stocks/government/lastcontracts.py | 95 +++++ discordbot/stocks/government/lasttrades.py | 145 ++++++++ discordbot/stocks/government/lobbying.py | 96 +++++ discordbot/stocks/government/qtrcontracts.py | 136 ++++++++ discordbot/stocks/government/topbuys.py | 149 ++++++++ discordbot/stocks/government/toplobbying.py | 85 +++++ discordbot/stocks/government/topsells.py | 162 +++++++++ 12 files changed, 1520 insertions(+) create mode 100644 discordbot/stocks/government/contracts.py create mode 100644 discordbot/stocks/government/gov_menu.py create mode 100644 discordbot/stocks/government/gtrades.py create mode 100644 discordbot/stocks/government/histcont.py create mode 100644 discordbot/stocks/government/lastcontracts.py create mode 100644 discordbot/stocks/government/lasttrades.py create mode 100644 discordbot/stocks/government/lobbying.py create mode 100644 discordbot/stocks/government/qtrcontracts.py create mode 100644 discordbot/stocks/government/topbuys.py create mode 100644 discordbot/stocks/government/toplobbying.py create mode 100644 discordbot/stocks/government/topsells.py diff --git a/discordbot/discordbot.py b/discordbot/discordbot.py index d2079f39483..80fd3b26dfb 100644 --- a/discordbot/discordbot.py +++ b/discordbot/discordbot.py @@ -35,6 +35,7 @@ gst_bot.load_extension("economy.economy_menu") gst_bot.load_extension("stocks.dark_pool_shorts.dps_menu") gst_bot.load_extension("stocks.technical_analysis.ta_menu") gst_bot.load_extension("stocks.due_diligence.dd_menu") +gst_bot.load_extension("stocks.government.gov_menu") # Get all command names all_cmds = gst_bot.all_commands.keys() diff --git a/discordbot/stocks/government/contracts.py b/discordbot/stocks/government/contracts.py new file mode 100644 index 00000000000..ff143b6a38b --- /dev/null +++ b/discordbot/stocks/government/contracts.py @@ -0,0 +1,94 @@ +import discord +import config_discordbot as cfg +from discordbot import gst_imgur +from matplotlib import pyplot as plt +import os +import pandas as pd + +from gamestonk_terminal.config_plot import PLOT_DPI +from gamestonk_terminal.stocks.government import quiverquant_model +from gamestonk_terminal.helper_funcs import plot_autoscale + + +async def contracts_command(ctx, ticker="", past_transaction_days="", raw=""): + """Displays contracts associated with tickers [quiverquant.com]""" + try: + # Debug user input + if cfg.DEBUG: + print(f"!stocks.gov.contracts {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 == "false" or raw == "False" or raw == "FALSE" or raw == "": + raw = False + elif raw == "true" or raw == "True" or raw == "TRUE": + raw = True + else: + 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: + print(f"Image URL: {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) diff --git a/discordbot/stocks/government/gov_menu.py b/discordbot/stocks/government/gov_menu.py new file mode 100644 index 00000000000..f02cc24315b --- /dev/null +++ b/discordbot/stocks/government/gov_menu.py @@ -0,0 +1,327 @@ +import asyncio +import discord +import config_discordbot as cfg + +# pylint: disable=wrong-import-order,too-many-branches +from discordbot import gst_bot + +from stocks.government.lasttrades import lasttrades_command +from stocks.government.topbuys import topbuys_command +from stocks.government.topsells import topsells_command +from stocks.government.lastcontracts import lastcontracts_command +from stocks.government.qtrcontracts import qtrcontracts_command +from stocks.government.toplobbying import toplobbying_command +from stocks.government.gtrades import gtrades_command +from stocks.government.contracts import contracts_command +from stocks.government.histcont import histcont_command +from stocks.government.lobbying import lobbying_command + + +class GovernmentCommands(discord.ext.commands.Cog): + """Government menu""" + + def __init__(self, bot: discord.ext.commands.Bot): + self.bot = bot + + @discord.ext.commands.command(name="stocks.gov.lasttrades") + async def lasttrades( + self, + ctx: discord.ext.commands.Context, + gov_type="", + past_transactions_days="", + representative="", + ): + """Displays trades made by the congress/senate/house [quiverquant.com] + + Parameters + ----------- + gov_type: str + Possible arguments: congress, senate & house + past_transactions_days: int + Positive number of past transaction days + representative: str + Enter name of a representative + """ + await lasttrades_command(ctx, gov_type, past_transactions_days, representative) + + @discord.ext.commands.command(name="stocks.gov.topbuys") + async def topbuys( + self, + ctx: discord.ext.commands.Context, + gov_type="", + past_transactions_months="", + num="", + raw="", + ): + """Displays most purchased stocks by the congress/senate/house [quiverquant.com] + + Parameters + ----------- + gov_type: str + Possible arguments: congress, senate & house + past_transactions_months: int + Positive number of past transaction months + num: int + Number of most sold stocks to retrieve + raw: boolean + True or false + """ + await topbuys_command(ctx, gov_type, past_transactions_months, num, raw) + + @discord.ext.commands.command(name="stocks.gov.topsells") + async def topsells( + self, + ctx: discord.ext.commands.Context, + gov_type="", + past_transactions_months="", + num="", + raw="", + ): + """Displays most sold stocks by the congress/senate/house [quiverquant.com] + + Parameters + ----------- + gov_type: str + Possible arguments: congress, senate & house + past_transactions_months: int + Positive number of past transaction months + num: int + Number of most sold stocks to retrieve + raw: boolean + True or false + """ + await topsells_command(ctx, gov_type, past_transactions_months, num, raw) + + @discord.ext.commands.command(name="stocks.gov.lastcontracts") + async def lastcontracts( + self, ctx: discord.ext.commands.Context, past_transactions_days="", num="" + ): + """Displays last government contracts [quiverquant.com] + + Parameters + ----------- + past_transactions_days: int + Positive number of past transaction days + num: int + Number of contracts + """ + await lastcontracts_command(ctx, past_transactions_days, num) + + @discord.ext.commands.command(name="stocks.gov.qtrcontracts") + async def qtrcontracts( + self, ctx: discord.ext.commands.Context, num="", analysis="" + ): + """Displays a look at government contracts [quiverquant.com] + + Parameters + ----------- + analysis: str + Possible arguments: total, upmom & downmom + num: int + Number of contracts + """ + await qtrcontracts_command(ctx, num, analysis) + + @discord.ext.commands.command(name="stocks.gov.toplobbying") + async def toplobbying(self, ctx: discord.ext.commands.Context, num="", raw=""): + """Displays top lobbying firms [quiverquant.com] + + Parameters + ----------- + num: int + Number to show + raw: boolean + True or false + """ + await toplobbying_command(ctx, num, raw) + + @discord.ext.commands.command(name="stocks.gov.gtrades") + async def gtrades( + self, + ctx: discord.ext.commands.Context, + ticker="", + gov_type="", + past_transactions_months="", + raw="", + ): + """Displays government trades [quiverquant.com] + + Parameters + ----------- + ticker: str + ticker, -h or help + gov_type: str + Possible arguments: congress, senate & house + past_transactions_months: int + Positive number of past transaction months + raw: boolean + True or false + """ + await gtrades_command(ctx, ticker, gov_type, past_transactions_months, raw) + + @discord.ext.commands.command(name="stocks.gov.contracts") + async def contracts( + self, + ctx: discord.ext.commands.Context, + ticker="", + past_transaction_days="", + raw="", + ): + """Displays contracts associated with tickers [quiverquant.com] + + Parameters + ----------- + ticker: str + ticker, -h or help + past_transaction_days: int + Number of past transaction months + raw: boolean + True or false + """ + await contracts_command(ctx, ticker, past_transaction_days, raw) + + @discord.ext.commands.command(name="stocks.gov.histcont") + async def histcont(self, ctx: discord.ext.commands.Context, ticker=""): + """Displays historical quarterly-contracts [quiverquant.com] + + Parameters + ----------- + ticker: str + ticker, -h or help + """ + await histcont_command(ctx, ticker) + + @discord.ext.commands.command(name="stocks.gov.lobbying") + async def lobbying(self, ctx: discord.ext.commands.Context, ticker="", num=""): + """Displays lobbying details [quiverquant.com] + + Parameters + ----------- + ticker: str + ticker, -h or help + num: int + Number of events + """ + await lobbying_command(ctx, ticker, num) + + @discord.ext.commands.command(name="stocks.gov") + async def government_menu(self, ctx: discord.ext.commands.Context, ticker=""): + """Stocks Context - Shows Government Menu + + Returns + ------- + Sends a message to the discord user with the commands from the gov context. + The user can then select a reaction to trigger a command. + """ + + if cfg.DEBUG: + print(f"\n!stocks.gov {ticker}") + + text = ( + "0️⃣ !stocks.gov.lasttrades " + "\n" + "1️⃣ !stocks.gov.topbuys " + " \n" + "2️⃣ !stocks.gov.topsells " + " \n" + "3️⃣ !stocks.gov.lastcontracts \n" + "4️⃣ !stocks.gov.qtrcontracts \n" + "5️⃣ !stocks.gov.toplobbying \n" + ) + if ticker: + text += ( + f"6️⃣ !stocks.gov.gtrades {ticker} " + f"\n" + f"7️⃣ !stocks.gov.contracts {ticker} \n" + f"8️⃣ !stocks.gov.histcont {ticker}\n" + f"9️⃣ !stocks.gov.lobbying {ticker} \n" + ) + else: + text += ( + "\nMore commands available when providing a ticker with:" + "\n!stocks.gov " + ) + + title = "Stocks: Government (GOV) Menu" + embed = discord.Embed(title=title, description=text, colour=cfg.COLOR) + embed.set_author( + name=cfg.AUTHOR_NAME, + icon_url=cfg.AUTHOR_ICON_URL, + ) + msg = await ctx.send(embed=embed) + + emoji_list = ["0️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣"] + + if ticker: + emoji_list += ["5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣"] + + for emoji in emoji_list: + await msg.add_reaction(emoji) + + def check(reaction, user): + return user == ctx.message.author and str(reaction.emoji) in emoji_list + + try: + reaction, _ = await gst_bot.wait_for( + "reaction_add", timeout=cfg.MENU_TIMEOUT, check=check + ) + if reaction.emoji == "0️⃣": + if cfg.DEBUG: + print("Reaction selected: 0") + await lasttrades_command(ctx) + elif reaction.emoji == "1️⃣": + if cfg.DEBUG: + print("Reaction selected: 1") + await topbuys_command(ctx) + elif reaction.emoji == "2️⃣": + if cfg.DEBUG: + print("Reaction selected: 2") + await topsells_command(ctx) + elif reaction.emoji == "3️⃣": + if cfg.DEBUG: + print("Reaction selected: 3") + await lastcontracts_command(ctx) + elif reaction.emoji == "4️⃣": + if cfg.DEBUG: + print("Reaction selected: 4") + await qtrcontracts_command(ctx) + elif reaction.emoji == "5️⃣": + if cfg.DEBUG: + print("Reaction selected: 5") + await toplobbying_command(ctx, ticker) + elif reaction.emoji == "6️⃣": + if cfg.DEBUG: + print("Reaction selected: 6") + await gtrades_command(ctx, ticker) + elif reaction.emoji == "7️⃣": + if cfg.DEBUG: + print("Reaction selected: 7") + await contracts_command(ctx, ticker) + elif reaction.emoji == "8️⃣": + if cfg.DEBUG: + print("Reaction selected: 8") + await histcont_command(ctx, ticker) + elif reaction.emoji == "9️⃣": + if cfg.DEBUG: + print("Reaction selected: 9") + await lobbying_command(ctx, ticker) + + for emoji in emoji_list: + await msg.remove_reaction(emoji, ctx.bot.user) + + except asyncio.TimeoutError: + for emoji in emoji_list: + await msg.remove_reaction(emoji, ctx.bot.user) + embed = discord.Embed( + description="Error timeout - you snooze you lose! 😋", + colour=cfg.COLOR, + title="TIMEOUT Stocks: Government (GOV) Menu", + ).set_author( + name=cfg.AUTHOR_NAME, + icon_url=cfg.AUTHOR_ICON_URL, + ) + await ctx.send(embed=embed) + + +def setup(bot: discord.ext.commands.Bot): + gst_bot.add_cog(GovernmentCommands(bot)) diff --git a/discordbot/stocks/government/gtrades.py b/discordbot/stocks/government/gtrades.py new file mode 100644 index 00000000000..5025726436f --- /dev/null +++ b/discordbot/stocks/government/gtrades.py @@ -0,0 +1,149 @@ +import discord +import config_discordbot as cfg +from discordbot import gst_imgur +from matplotlib import pyplot as plt +import matplotlib.dates as mdates +from datetime import datetime, timedelta +import os +import pandas as pd + +from gamestonk_terminal.config_plot import PLOT_DPI +from gamestonk_terminal.stocks.government import quiverquant_model +from gamestonk_terminal.helper_funcs import plot_autoscale + + +async def gtrades_command( + ctx, ticker="", gov_type="", past_transactions_months="", raw="" +): + """Displays government trades [quiverquant.com]""" + try: + # Debug user input + if cfg.DEBUG: + print( + f"!stocks.gov.gtrades {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 == "false" or raw == "False" or raw == "FALSE" or raw == "": + raw = False + elif raw == "true" or raw == "True" or raw == "TRUE": + raw = True + else: + 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: + print(f"No recent {gov_type} trading data found\n") + 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: + print(f"Image URL: {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) diff --git a/discordbot/stocks/government/histcont.py b/discordbot/stocks/government/histcont.py new file mode 100644 index 00000000000..40c8c2bee76 --- /dev/null +++ b/discordbot/stocks/government/histcont.py @@ -0,0 +1,81 @@ +import discord +import config_discordbot as cfg +from discordbot import gst_imgur +from matplotlib import pyplot as plt +import os +import numpy as np + +from gamestonk_terminal.config_plot import PLOT_DPI +from gamestonk_terminal.stocks.government import quiverquant_model +from gamestonk_terminal.helper_funcs import plot_autoscale + + +async def histcont_command(ctx, ticker=""): + """Displays historical quarterly-contracts [quiverquant.com]""" + try: + # Debug user input + if cfg.DEBUG: + print(f"!stocks.gov.histcont {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: + print("No quarterly government contracts found\n") + 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: + print(f"Image URL: {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) diff --git a/discordbot/stocks/government/lastcontracts.py b/discordbot/stocks/government/lastcontracts.py new file mode 100644 index 00000000000..da38f2fc3af --- /dev/null +++ b/discordbot/stocks/government/lastcontracts.py @@ -0,0 +1,95 @@ +import discord +import config_discordbot as cfg +from helpers import pagination +import pandas as pd + +from gamestonk_terminal.stocks.government import quiverquant_model + + +async def lastcontracts_command(ctx, past_transactions_days="", num=""): + """Displays last government contracts [quiverquant.com]""" + try: + # Debug user input + if cfg.DEBUG: + print(f"!stocks.gov.lastcontracts {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: + print("No government contracts found\n") + return + + df_contracts.sort_values("Date", ascending=False) + + df_contracts["Date"] = pd.to_datetime(df_contracts["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", "Description", "Agency"] + ][:num] + df_contracts_str = df_contracts.to_string() + if len(df_contracts_str) <= 4000: + embed = discord.Embed( + title="Stocks: [quiverquant.com] Top buy government trading", + description="```" + df_contracts_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(df_contracts_str) / 4000: + columns.append( + discord.Embed( + title="Stocks: [quiverquant.com] Top buy government trading", + description="```" + df_contracts_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="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) diff --git a/discordbot/stocks/government/lasttrades.py b/discordbot/stocks/government/lasttrades.py new file mode 100644 index 00000000000..2d4b519aae7 --- /dev/null +++ b/discordbot/stocks/government/lasttrades.py @@ -0,0 +1,145 @@ +import discord +import config_discordbot as cfg +from helpers import pagination + +from gamestonk_terminal.stocks.government import quiverquant_model + + +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: + print( + f"!stocks.gov.lasttrades {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())}" + ) + else: + embed = discord.Embed( + title=f"Stocks: [quiverquant.com] Trades by {representative}", + description="```" + df_gov_rep.to_string(index=False) + "```", + colour=cfg.COLOR, + ) + embed.set_author( + name=cfg.AUTHOR_NAME, + icon_url=cfg.AUTHOR_ICON_URL, + ) + + await ctx.send(embed=embed) + else: + df_gov_str = df_gov.to_string(index=False) + if len(df_gov_str) <= 4000: + embed = discord.Embed( + title=f"Stocks: [quiverquant.com] Last transactions for {gov_type.upper()}", + description="```" + df_gov_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(gov_type) / 4000: + columns.append( + discord.Embed( + title=f"Stocks: [quiverquant.com] Last transactions for {gov_type.upper()}", + description="```" + gov_type[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: [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) diff --git a/discordbot/stocks/government/lobbying.py b/discordbot/stocks/government/lobbying.py new file mode 100644 index 00000000000..8ced6977348 --- /dev/null +++ b/discordbot/stocks/government/lobbying.py @@ -0,0 +1,96 @@ +import discord +import config_discordbot as cfg +from helpers import pagination + +from gamestonk_terminal.stocks.government import quiverquant_model + + +async def lobbying_command(ctx, ticker="", num=""): + """Displays lobbying details [quiverquant.com]""" + + try: + # Debug user input + if cfg.DEBUG: + print(f"!stocks.gov.lobbying {ticker}") + + if ticker == "": + raise Exception("A ticker is required") + + if num == "": + num = 10 + else: + if not num.lstrip("-").isnumeric(): + raise Exception("Number has to be an integer") + num = int(num) + + # Retrieve Data + df_lobbying = quiverquant_model.get_government_trading( + "corporate-lobbying", ticker=ticker + ) + + if df_lobbying.empty: + print("No corporate lobbying found\n") + return + + # Output Data + report = "" + for _, row in ( + df_lobbying.sort_values(by=["Date"], ascending=False).head(num).iterrows() + ): + amount = ( + "$" + str(int(float(row["Amount"]))) + if row["Amount"] is not None + else "N/A" + ) + report += f"{row['Date']}: {row['Client']} {amount}" + if row["Amount"] is not None: + report += "\t" + row["Specific_Issue"].replace("\n", " ").replace( + "\r", "" + ) + report += "\n" + + if len(report) <= 4000: + embed = discord.Embed( + title=f"Stocks: [quiverquant.com] {ticker.upper()} Lobbying Details", + description="```" + report + "```", + 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(report) / 4000: + columns.append( + discord.Embed( + title=f"Stocks: [quiverquant.com] {ticker.upper()} Lobbying Details", + description="```" + report[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: [quiverquant.com] {ticker.upper()} Lobbying Details", + colour=cfg.COLOR, + description=e, + ) + embed.set_author( + name=cfg.AUTHOR_NAME, + icon_url=cfg.AUTHOR_ICON_URL, + ) + + await ctx.send(embed=embed) diff --git a/discordbot/stocks/government/qtrcontracts.py b/discordbot/stocks/government/qtrcontracts.py new file mode 100644 index 00000000000..8b871f482df --- /dev/null +++ b/discordbot/stocks/government/qtrcontracts.py @@ -0,0 +1,136 @@ +import discord +import config_discordbot as cfg +from discordbot import gst_imgur +from matplotlib import pyplot as plt +import os +import numpy as np + +from gamestonk_terminal.config_plot import PLOT_DPI +from gamestonk_terminal.stocks.government import quiverquant_model +from gamestonk_terminal.helper_funcs import plot_autoscale + + +async def qtrcontracts_command(ctx, num="", analysis=""): + """Displays a look at government contracts [quiverquant.com]""" + try: + # Debug user input + if cfg.DEBUG: + print(f"!stocks.gov.qtrcontracts {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: + print(f"Image URL: {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": + description = tickers.to_string() + embed = discord.Embed( + title="Stocks: [quiverquant.com] Government contracts", + description=description, + 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) diff --git a/discordbot/stocks/government/topbuys.py b/discordbot/stocks/government/topbuys.py new file mode 100644 index 00000000000..337b4ac953a --- /dev/null +++ b/discordbot/stocks/government/topbuys.py @@ -0,0 +1,149 @@ +import discord +import config_discordbot as cfg +from discordbot import gst_imgur +from datetime import datetime, timedelta +from matplotlib import pyplot as plt +import os +import pandas as pd + +from gamestonk_terminal.config_plot import PLOT_DPI +from gamestonk_terminal.stocks.government import quiverquant_model +from gamestonk_terminal.helper_funcs import plot_autoscale + + +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: + print( + f"!stocks.gov.topbuys {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 == "false" or raw == "False" or raw == "FALSE" or raw == "": + raw = False + elif raw == "true" or raw == "True" or raw == "TRUE": + raw = True + else: + 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: + print(f"No {gov_type} trading data found\n") + 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: + print(f"Image URL: {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) diff --git a/discordbot/stocks/government/toplobbying.py b/discordbot/stocks/government/toplobbying.py new file mode 100644 index 00000000000..89f68bb615f --- /dev/null +++ b/discordbot/stocks/government/toplobbying.py @@ -0,0 +1,85 @@ +import discord +import config_discordbot as cfg +from discordbot import gst_imgur +from matplotlib import pyplot as plt +import os +import pandas as pd + +from gamestonk_terminal.config_plot import PLOT_DPI +from gamestonk_terminal.stocks.government import quiverquant_model +from gamestonk_terminal.helper_funcs import plot_autoscale + + +async def toplobbying_command(ctx, num="", raw=""): + """Displays top lobbying firms [quiverquant.com]""" + try: + # Debug user input + if cfg.DEBUG: + print(f"!stocks.gov.toplobbying {num} {raw}") + + if num == "": + num = 10 + else: + if not num.lstrip("-").isnumeric(): + raise Exception("Number has to be an integer") + num = int(num) + + if raw == "false" or raw == "False" or raw == "FALSE" or raw == "": + raw = False + elif raw == "true" or raw == "True" or raw == "TRUE": + raw = True + else: + raise Exception("raw argument has to be true or false") + + # Retrieve Data + df_lobbying = quiverquant_model.get_government_trading("corporate-lobbying") + + if df_lobbying.empty: + raise Exception("No corporate lobbying found") + + df_lobbying["Amount"] = df_lobbying.Amount.astype(float).fillna(0) / 100_000 + + lobbying_by_ticker = pd.DataFrame( + df_lobbying.groupby("Ticker")["Amount"].agg("sum") + ).sort_values(by="Amount", ascending=False) + fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) + lobbying_by_ticker.head(num).plot(kind="bar", ax=ax) + ax.set_xlabel("Ticker") + ax.set_ylabel("Total Amount ($100k)") + ax.set_title(f"Corporate Lobbying Spent since {df_lobbying['Date'].min()}") + fig.tight_layout() + + plt.savefig("ta_toplobbying.png") + uploaded_image = gst_imgur.upload_image("ta_toplobbying.png", title="something") + image_link = uploaded_image.link + if cfg.DEBUG: + print(f"Image URL: {image_link}") + title = "Stocks: [quiverquant.com] Top Lobbying Firms" + if raw: + description = lobbying_by_ticker.head(num).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("ta_toplobbying.png") + + await ctx.send(embed=embed) + + except Exception as e: + embed = discord.Embed( + title="ERROR Stocks: [quiverquant.com] Top Lobbying Firms", + colour=cfg.COLOR, + description=e, + ) + embed.set_author( + name=cfg.AUTHOR_NAME, + icon_url=cfg.AUTHOR_ICON_URL, + ) + + await ctx.send(embed=embed) diff --git a/discordbot/stocks/government/topsells.py b/discordbot/stocks/government/topsells.py new file mode 100644 index 00000000000..65d4936a41e --- /dev/null +++ b/discordbot/stocks/government/topsells.py @@ -0,0 +1,162 @@ +import discord +import config_discordbot as cfg +from discordbot import gst_imgur +from datetime import datetime, timedelta +from matplotlib import pyplot as plt +import os +import pandas as pd + +from gamestonk_terminal.config_plot import PLOT_DPI +from gamestonk_terminal.stocks.government import quiverquant_model +from gamestonk_terminal.helper_funcs import plot_autoscale + + +async def topsells_command( + ctx, gov_type="", past_transactions_months="", num="", raw="" +): + """Displays most sold stocks by the congress/senate/house [quiverquant.com]""" + try: + # Debug user input + if cfg.DEBUG: + print( + f"!stocks.gov.topsells {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 == "false" or raw == "False" or raw == "FALSE" or raw == "": + raw = False + elif raw == "true" or raw == "True" or raw == "TRUE": + raw = True + else: + 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) + + # Output Data + if df_gov.empty: + raise Exception(f"No {gov_type} trading data found\n") + + 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() + + 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() + .replace(">$", "") + .strip() + ) + df_gov["max"] = df_gov["Range"].apply( + lambda x: x.split("-")[1] + .replace(",", "") + .strip() + .strip("$") + .replace(">$", "") + .strip() + if "-" in x + else x.strip("$").replace(",", "").replace(">$", "").strip() + ) + + 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=True) + .abs() + .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().abs().head( + n=num + ).plot(kind="bar", rot=0, ax=ax) + ax.set_ylabel("Amount ($1k)") + ax.set_title( + f"{num} most sold stocks over last {past_transactions_months} months" + f" (upper bound) for {gov_type}" + ) + plt.gcf().axes[0].yaxis.get_major_formatter().set_scientific(False) + fig.tight_layout() + + plt.savefig("gov_topsells.png") + uploaded_image = gst_imgur.upload_image("gov_topsells.png", title="something") + image_link = uploaded_image.link + if cfg.DEBUG: + print(f"Image URL: {image_link}") + title = f"Stocks: [quiverquant.com] Top sells 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_topsells.png") + + await ctx.send(embed=embed) + + except Exception as e: + embed = discord.Embed( + title=f"ERROR Stocks: [quiverquant.com] Top sells 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)