async def sidtc_command(ctx, sort="float", num: int = 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 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 = [] choices = [ disnake.SelectOption(label="Overview", value="0", emoji="🟢"), ] initial_str = "Overview" i = 1 for col_name in df.columns.values: menu = f"\nPage {i}: {col_name}" initial_str += f"\nPage {i}: {col_name}" if i < 19: choices.append( disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"), ) if i == 20: choices.append( disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"), ) i += 1 columns.append( disnake.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( disnake.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 ctx.send(embed=columns[0], view=Menu(columns, choices)) except Exception as e: embed = disnake.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, delete_after=30.0)
async def contracts_command(ctx, ticker: str = "", past_transaction_days: int = 10, raw: bool = False): """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 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) plt.style.use("seaborn") 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") imagefile = "gov_contracts.png" img = Image.open(imagefile) print(img.size) im_bg = Image.open(cfg.IMG_BG) h = img.height + 240 w = img.width + 520 img = img.resize((w, h), Image.ANTIALIAS) x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0]) y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1]) x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0]) y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1]) img = img.convert("RGB") im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2)) im_bg.save(imagefile, "PNG", quality=100) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) 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 = disnake.Embed(title=title, description=description, colour=cfg.COLOR) else: embed = disnake.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 = disnake.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, delete_after=30.0)
async def performance_command(ctx, economy_group="sector"): """Performance 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("econ-performance %s", economy_group) # Select default group if not 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: possible_group_list = ", ".join(possible_groups) raise Exception( f"Select a valid group from {possible_group_list}") # nosec group = d_economy_group[economy_group] # Retrieve data df_group = finviz_model.get_valuation_performance_data( group, "performance") # Check for argument if df_group.empty: raise Exception("No available data found") # Output data df = pd.DataFrame(df_group) df = df.replace(np.nan, 0) df["Volume"] = df["Volume"] / 1_000_000 df["Avg Volume"] = df["Avg Volume"] / 1_000_000 formats = { "Perf Month": "{:.2f}", "Perf Quart": "{:.2f}", "Perf Half": "{:.2f}", "Perf Year": "{:.2f}", "Perf YTD": "{:.2f}", "Avg Volume": "{:.0f}M", "Change": "{:.2f}", "Volume": "{:.0f}M", } for col, value in formats.items(): df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640 df = df.set_axis( [ "Name", "Week", "Month", "3Month", "6Month", "1Year", "YTD", "Recom", "Avg Vol.", "RelVolume", "Change", "Volume", ], axis="columns", ) df = df.fillna("") df.set_index("Name", inplace=True) dindex = len(df.index) fig = df2img.plot_dataframe( df, fig_size=(1500, (40 + (50 * dindex))), col_width=[10, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3.5], tbl_cells=dict( align=["left", "center"], height=35, ), template="plotly_dark", font=dict( family="Consolas", size=20, ), paper_bgcolor="rgba(0, 0, 0, 0)", ) imagefile = "econ-performance.png" df2img.save_dataframe(fig=fig, filename=imagefile) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) image = disnake.File(imagefile) title = f"Economy: [Finviz] Performance {group}" embed = disnake.Embed(title=title, colour=cfg.COLOR) embed.set_image(url=f"attachment://{imagefile}") embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) os.remove(imagefile) await ctx.send(embed=embed, file=image) except Exception as e: embed = disnake.Embed( title="ERROR Economy: [Finviz] Performance", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0)
async def ford_command(ctx): """Display Orders by Fidelity Customers. [Source: Fidelity] Parameters ---------- num: Number of stocks to display """ try: # Debug if cfg.DEBUG: logger.debug("!ford") order_header, df_orders = fidelity_model.get_orders() df_orders = df_orders.head(n=30).iloc[:, :-1] df_orders = df_orders.applymap(str) font_color = (["white"] * 2 + [[ "#ad0000" if boolv else "#0d5700" for boolv in df_orders["Price Change"].str.contains("-") ]] + [["white"] * 3]) df_orders.set_index("Symbol", inplace=True) df_orders = df_orders.apply(lambda x: x.str.slice(0, 20)) dindex = len(df_orders.index) fig = df2img.plot_dataframe( df_orders, fig_size=(1500, (40 + (40 * dindex))), col_width=[1, 3, 2.2, 3, 2, 2], tbl_cells=dict( align=["left", "center", "left", "left", "center"], font=dict(color=font_color), height=35, ), template="plotly_dark", font=dict( family="Consolas", size=20, ), paper_bgcolor="rgba(0, 0, 0, 0)", ) imagefile = "disc-ford.png" df2img.save_dataframe(fig=fig, filename=imagefile) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) image = disnake.File(imagefile) title = "Fidelity Customer Orders" embed = disnake.Embed(title=title, colour=cfg.COLOR) embed.set_image(url=f"attachment://{imagefile}") embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) os.remove(imagefile) await ctx.send(embed=embed, file=image) except Exception as e: embed = disnake.Embed( title="ERROR Fidelity Customer Orders", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0)
async def analyst_command(ctx, ticker=""): """Displays analyst recommendations [Finviz]""" try: # Debug if cfg.DEBUG: logger.debug("!stocks.dd.analyst %s", ticker) # Check for argument if not ticker: raise Exception("Stock ticker is required") df = finviz_model.get_analyst_data(ticker) df = df.replace(np.nan, 0) df.index.names = ["Date"] df = df.rename( columns={ "category": "Category", "analyst": "Analyst", "rating": "Rating", "target": "Target", "target_from": "Target From", "target_to": "Target To", }) dindex = len(df.index) fig = df2img.plot_dataframe( df, fig_size=(1500, (40 + (40 * dindex))), col_width=[5, 5, 8, 14, 5, 5, 5], tbl_cells=dict(height=35, ), font=dict( family="Consolas", size=20, ), template="plotly_dark", paper_bgcolor="rgba(0, 0, 0, 0)", ) imagefile = "dd-analyst.png" df2img.save_dataframe(fig=fig, filename=imagefile) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) image = disnake.File(imagefile) title = f"Stocks: [Finviz] Analyst Recommendations {ticker.upper()}" embed = disnake.Embed(title=title, colour=cfg.COLOR) embed.set_image(url=f"attachment://{imagefile}") embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) os.remove(imagefile) await ctx.send(embed=embed, file=image) except Exception as e: embed = disnake.Embed( title="ERROR Stocks: [Finviz] Analyst Recommendations", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0)
async def gtrades_command( ctx, ticker: str = "", gov_type="", past_transactions_months: int = 10, raw: bool = False, ): """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 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) plt.style.use("seaborn") 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") imagefile = "gov_gtrades.png" img = Image.open(imagefile) print(img.size) im_bg = Image.open(cfg.IMG_BG) h = img.height + 240 w = img.width + 520 img = img.resize((w, h), Image.ANTIALIAS) x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0]) y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1]) x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0]) y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1]) img = img.convert("RGB") im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2)) im_bg.save(imagefile, "PNG", quality=100) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) 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 = disnake.Embed(title=title, description=description, colour=cfg.COLOR) else: embed = disnake.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 = disnake.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, delete_after=30.0)
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) ] plt.style.use("seaborn") 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") imagefile = "gov_histcont.png" img = Image.open(imagefile) print(img.size) im_bg = Image.open(cfg.IMG_BG) h = img.height + 240 w = img.width + 520 img = img.resize((w, h), Image.ANTIALIAS) x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0]) y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1]) x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0]) y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1]) img = img.convert("RGB") im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2)) im_bg.save(imagefile, "PNG", quality=100) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) 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 = disnake.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 = disnake.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, delete_after=30.0)
async def psi_command(ctx, ticker: str = ""): """Price vs short interest volume [Stockgrid]""" try: # Debug user input if cfg.DEBUG: logger.debug("!stocks.dps.psi %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, prices = stockgrid_model.get_short_interest_volume(ticker) # Debug user output if cfg.DEBUG: logger.debug(df.to_string()) # Output data title = f"Stocks: [Stockgrid] Price vs Short Interest Volume {ticker}" embed = disnake.Embed(title=title, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) plt.style.use("seaborn") _, axes = plt.subplots( 2, 1, figsize=plot_autoscale(), dpi=PLOT_DPI, gridspec_kw={"height_ratios": [2, 1]}, ) axes[0].bar( df["date"], df["total_volume"] / 1_000_000, width=timedelta(days=1), color="b", alpha=0.4, label="Total Volume", ) axes[0].bar( df["date"], df["short_volume"] / 1_000_000, width=timedelta(days=1), color="r", alpha=0.4, label="Short Volume", ) axes[0].set_ylabel("Volume (1M)") ax2 = axes[0].twinx() ax2.plot( df["date"].values, prices[len(prices) - len(df):], # noqa: E203 c="k", label="Price", ) ax2.set_ylabel("Price ($)") lines, labels = axes[0].get_legend_handles_labels() lines2, labels2 = ax2.get_legend_handles_labels() ax2.legend(lines + lines2, labels + labels2, loc="upper left") axes[0].grid() axes[0].ticklabel_format(style="plain", axis="y") plt.title(f"Price vs Short Volume Interest for {ticker}") plt.gcf().autofmt_xdate() axes[1].plot( df["date"].values, 100 * df["short_volume%"], c="green", label="Short Vol. %", ) axes[1].set_ylabel("Short Vol. %") axes[1].grid(axis="y") lines, labels = axes[1].get_legend_handles_labels() axes[1].legend(lines, labels, loc="upper left") axes[1].set_ylim([0, 100]) file_name = ticker + "_psi.png" plt.savefig(file_name) imagefile = file_name img = Image.open(imagefile) print(img.size) im_bg = Image.open(cfg.IMG_BG) h = img.height + 240 w = img.width + 520 img = img.resize((w, h), Image.ANTIALIAS) x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0]) y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1]) x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0]) y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1]) img = img.convert("RGB") im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2)) im_bg.save(imagefile, "PNG", quality=100) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) 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 = disnake.Embed( title= f"ERROR Stocks: [Stockgrid] Price vs Short Interest Volume {ticker}", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0)
async def ftd_command(ctx, ticker: str = "", 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() 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()) stock = discordbot.helpers.load(ticker, start) stock_ftd = stock[stock.index > start] stock_ftd = stock_ftd[stock_ftd.index < end] # Output data fig = make_subplots(shared_xaxes=True, specs=[[{"secondary_y": True}]]) fig.add_trace( go.Scatter( name=ticker, x=stock_ftd.index, y=stock_ftd["Adj Close"], line=dict(color="#fdc708", width=2), opacity=1, showlegend=False, ), secondary_y=False, ) fig.add_trace( go.Bar( name="FTDs", x=ftds_data["SETTLEMENT DATE"], y=ftds_data["QUANTITY (FAILS)"] / 1000, opacity=1, ), secondary_y=True, ) # Set y-axes titles fig.update_yaxes(title_text="<b>Shares</b> [K]", secondary_y=True) fig.update_layout( margin=dict(l=0, r=20, t=30, b=20), template=cfg.PLT_TA_STYLE_TEMPLATE, colorway=cfg.PLT_TA_COLORWAY, title=f"{ticker}", title_x=0.5, yaxis_title="<b>Stock Price</b> ($)", yaxis=dict( side="right", fixedrange=False, ), xaxis=dict( rangeslider=dict(visible=False), type="date", fixedrange=False, ), xaxis2=dict( rangeslider=dict(visible=False), type="date", fixedrange=False, ), dragmode="pan", legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1), yaxis2=dict( side="left", position=0.15, fixedrange=False, ), hovermode="x unified", ) config = dict({"scrollZoom": True}) imagefile = "dps_ftd.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = random.randint(69, 69420) fig.write_html(f"in/ftds_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/ftds_{html_ran}.html)" fig.update_layout( width=800, height=500, ) fig.write_image(imagefile) img = Image.open(imagefile) print(img.size) im_bg = Image.open(cfg.IMG_BG) h = img.height + 240 w = img.width + 520 # Paste fig onto background img and autocrop background img = img.resize((w, h), Image.ANTIALIAS) x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0]) y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1]) x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0]) y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1]) img = img.convert("RGB") im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2)) im_bg.save(imagefile, "PNG", quality=100) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) image = disnake.File(imagefile) print(f"Image {imagefile}") if cfg.DEBUG: logger.debug("Image: %s", imagefile) title = "Stocks: [SEC] Failure-to-deliver " + ticker embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR) embed.set_image(url=f"attachment://{imagefile}") embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) os.remove(imagefile) await ctx.send(embed=embed, file=image) except Exception as e: embed = disnake.Embed( title="ERROR Stocks: [SEC] Failure-to-deliver", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0)
async def hsi_command(ctx, num: int = 10): """Show top high short interest stocks of over 20% ratio [shortinterest.com]""" try: # Debug user input if cfg.DEBUG: logger.debug("!stocks.dps.hsi %s", num) # Check for argument if num < 0: raise Exception("Number has to be above 0") # Retrieve data df = shortinterest_model.get_high_short_interest() df = df.iloc[1:].head(n=num) # Debug user output if cfg.DEBUG: logger.debug(df.to_string()) # Output data future_column_name = df["Ticker"] df = df.transpose() df.columns = future_column_name df.drop("Ticker") columns = [] choices = [ disnake.SelectOption(label="Overview", value="0", emoji="🟢"), ] initial_str = "Overview" i = 1 for col_name in df.columns.values: menu = f"\nPage {i}: {col_name}" initial_str += f"\nPage {i}: {col_name}" choices.append( disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"), ) i += 1 columns.append( disnake.Embed( title="Stocks: [highshortinterest.com] Top High Short Interest", 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( disnake.Embed( title= "Stocks: [highshortinterest.com] Top High Short Interest", description="```" + df[column].fillna("").to_string() + "```", colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) await ctx.send(embed=columns[0], view=Menu(columns, choices)) except Exception as e: embed = disnake.Embed( title= "ERROR Stocks: [highshortinterest.com] Top High Short Interest", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0)
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) trace_name = df_ta.columns[0].replace("_", " ") fig = make_subplots( rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.07, row_width=[0.5, 0.6], ) fig.add_trace( go.Scatter( name=ticker, x=df_stock.index, y=df_stock["Adj Close"].values, line=dict(color="#fdc708", width=2), opacity=1, showlegend=False, ), row=1, col=1, ) fig.add_trace( go.Bar( name="MACD Histogram", x=df_ta.index, y=df_ta.iloc[:, 1].values, opacity=1, ), row=2, col=1, ) fig.add_trace( go.Scatter( mode="lines", name="MACD Line", x=df_ta.index, y=df_ta.iloc[:, 0].values, opacity=1, ), row=2, col=1, ) fig.add_trace( go.Scatter( mode="lines", name="Signal Line", x=df_ta.index, y=df_ta.iloc[:, 2].values, opacity=1, ), row=2, col=1, ) fig.update_layout( margin=dict(l=0, r=20, t=30, b=20), template=cfg.PLT_TA_STYLE_TEMPLATE, colorway=cfg.PLT_TA_COLORWAY, title=f"{ticker} {trace_name}", title_x=0.3, yaxis_title="Stock Price ($)", yaxis=dict(fixedrange=False, ), xaxis=dict( rangeslider=dict(visible=False), type="date", ), dragmode="pan", legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1), ) config = dict({"scrollZoom": True}) imagefile = "ta_macd.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = random.randint(69, 69420) fig.write_html(f"in/macd_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/macd_{html_ran}.html)" fig.update_layout( width=800, height=500, ) fig.write_image(imagefile) img = Image.open(imagefile) print(img.size) im_bg = Image.open(cfg.IMG_BG) h = img.height + 240 w = img.width + 520 # Paste fig onto background img and autocrop background img = img.resize((w, h), Image.ANTIALIAS) x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0]) y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1]) x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0]) y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1]) img = img.convert("RGB") im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2)) im_bg.save(imagefile, "PNG", quality=100) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) image = disnake.File(imagefile) print(f"Image {imagefile}") if cfg.DEBUG: logger.debug("Image: %s", imagefile) title = "Stocks: Moving-Average-Convergence-Divergence " + ticker embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR) embed.set_image(url=f"attachment://{imagefile}") embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) os.remove(imagefile) await ctx.send(embed=embed, file=image) except Exception as e: embed = disnake.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, delete_after=30.0)
async def qtrcontracts_command(ctx, num: int = 20, 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) 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) plt.style.use("seaborn") # 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") imagefile = "gov_qtrcontracts.png" img = Image.open(imagefile) print(img.size) im_bg = Image.open(cfg.IMG_BG) h = img.height + 240 w = img.width + 520 img = img.resize((w, h), Image.ANTIALIAS) x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0]) y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1]) x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0]) y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1]) img = img.convert("RGB") im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2)) im_bg.save(imagefile, "PNG", quality=100) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) 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 = disnake.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]"] embed = disnake.Embed( title="Stocks: [quiverquant.com] Government contracts", description=tickers.to_string(), 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 = disnake.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, delete_after=30.0)
async def est_command(ctx, ticker: str = ""): """Displays earning estimates [Business Insider]""" try: # Debug if cfg.DEBUG: logger.debug("!stocks.dd.est %s", ticker) # Check for argument if ticker == "": raise Exception("Stock ticker is required") ( df_year_estimates, df_quarter_earnings, df_quarter_revenues, ) = business_insider_model.get_estimates(ticker) if (df_quarter_revenues.empty and df_year_estimates.empty and df_quarter_earnings.empty): raise Exception("Enter a valid ticker") # Debug user output if cfg.DEBUG: logger.debug(df_year_estimates.to_string()) logger.debug(df_quarter_earnings.to_string()) logger.debug(df_quarter_revenues.to_string()) dindex = len(df_year_estimates.index) fig = df2img.plot_dataframe( df_year_estimates, fig_size=(1200, (40 + (60 * dindex))), col_width=[12, 4, 4, 4, 4], tbl_cells=dict(height=35, ), font=dict( family="Consolas", size=20, ), template="plotly_dark", paper_bgcolor="rgba(0, 0, 0, 0)", ) imagefile = "estimates.png" df2img.save_dataframe(fig=fig, filename=imagefile) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) uploaded_image = gst_imgur.upload_image(imagefile, title="something") link_estimates = uploaded_image.link os.remove(imagefile) fig = df2img.plot_dataframe( df_quarter_earnings, fig_size=(1200, (40 + (40 * 20))), col_width=[5, 5, 4, 4, 5, 4], tbl_cells=dict(height=35, ), font=dict( family="Consolas", size=20, ), template="plotly_dark", paper_bgcolor="rgba(0, 0, 0, 0)", ) imagefile = "earnings.png" df2img.save_dataframe(fig=fig, filename=imagefile) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) uploaded_image = gst_imgur.upload_image(imagefile, title="something") link_earnings = uploaded_image.link os.remove(imagefile) fig = df2img.plot_dataframe( df_quarter_revenues, fig_size=(1200, (40 + (40 * 20))), col_width=[5, 5, 4, 4, 5, 4], tbl_cells=dict(height=35, ), font=dict( family="Consolas", size=20, ), template="plotly_dark", paper_bgcolor="rgba(0, 0, 0, 0)", ) imagefile = "revenues.png" df2img.save_dataframe(fig=fig, filename=imagefile) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) uploaded_image = gst_imgur.upload_image(imagefile, title="something") link_revenues = uploaded_image.link os.remove(imagefile) embeds = [ disnake.Embed( title=f"**{ticker.upper()} Year Estimates**", color=cfg.COLOR, ), disnake.Embed( title=f"**{ticker.upper()} Quarter Earnings**", colour=cfg.COLOR, ), disnake.Embed( title=f"**{ticker.upper()} Quarter Revenues**", colour=cfg.COLOR, ), ] embeds[0].set_image(url=link_estimates) embeds[1].set_image(url=link_earnings) embeds[2].set_image(url=link_revenues) # Output data choices = [ disnake.SelectOption(label=f"{ticker.upper()} Year Estimates", value="0", emoji="🟢"), disnake.SelectOption(label=f"{ticker.upper()} Quarter Earnings", value="1", emoji="🟢"), disnake.SelectOption(label=f"{ticker.upper()} Quarter Revenues", value="2", emoji="🟢"), ] await ctx.send(embed=embeds[0], view=Menu(embeds, choices)) except Exception as e: embed = disnake.Embed( title="ERROR Stocks: [Business Insider] Earning Estimates", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0)
async def lins_command(ctx, ticker: str = "", num: int = 10): """Display insider activity for a given stock ticker. [Source: Finviz] Parameters ---------- ticker : Stock Ticker num : Number of latest insider activity to display """ try: # Debug if cfg.DEBUG: logger.debug("disc-lins %s", num) d_finviz_insider = finviz_model.get_last_insider_activity(ticker) df = pd.DataFrame.from_dict(d_finviz_insider) df.set_index("Date", inplace=True) df = df[ [ "Relationship", "Transaction", "#Shares", "Cost", "Value ($)", "#Shares Total", "Insider Trading", "SEC Form 4", ] ] df = df.head(num) df = df.replace(to_replace="Option Exercise", value="Opt Ex.", regex=True) title = f"Insider Trading for {ticker.upper()}" embeds: list = [] i, i2, end = 0, 0, 20 df_pg = [] embeds_img = [] dindex = len(df.index) while i < dindex: df_pg = df.iloc[i:end] df_pg.append(df_pg) fig = df2img.plot_dataframe( df_pg, fig_size=(1600, (40 + (40 * 20))), col_width=[5, 14, 4, 4, 3, 4, 5, 8, 7], tbl_cells=dict( height=35, ), font=dict( family="Consolas", size=20, ), template="plotly_dark", paper_bgcolor="rgba(0, 0, 0, 0)", ) imagefile = f"disc-insider{i}.png" df2img.save_dataframe(fig=fig, filename=imagefile) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) uploaded_image = gst_imgur.upload_image(imagefile, title="something") image_link = uploaded_image.link embeds_img.append( f"{image_link}", ) embeds.append( disnake.Embed( title=title, colour=cfg.COLOR, ), ) i2 += 1 i += 20 end += 20 os.remove(imagefile) # Author/Footer for i in range(0, i2): embeds[i].set_author( name=cfg.AUTHOR_NAME, url=cfg.AUTHOR_URL, icon_url=cfg.AUTHOR_ICON_URL, ) embeds[i].set_footer( text=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) i = 0 for i in range(0, i2): embeds[i].set_image(url=embeds_img[i]) i += 1 embeds[0].set_footer(text=f"Page 1 of {len(embeds)}") options = [ disnake.SelectOption(label="Home", value="0", emoji="🟢"), ] await ctx.send(embed=embeds[0], view=Menu(embeds, options)) except Exception as e: embed = disnake.Embed( title="ERROR Insider Activity [Source: Finviz]", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0)
async def lobbying_command(ctx, ticker="", num: int = 10): """Displays lobbying details [quiverquant.com]""" try: # Debug user input if cfg.DEBUG: logger.debug("!stocks.gov.lobbying %s", ticker) if ticker == "": raise Exception("A ticker is required") # Retrieve Data df_lobbying = quiverquant_model.get_government_trading( "corporate-lobbying", ticker=ticker) if df_lobbying.empty: logger.debug("No corporate lobbying found") return # Output Data report = "" choices = [ disnake.SelectOption(label="Overview", value="0", emoji="🟢"), ] 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 = disnake.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( disnake.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 ctx.send(embed=columns[0], view=Menu(columns, choices)) except Exception as e: embed = disnake.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, delete_after=30.0)
async def iv_command(ctx, ticker: str = None): """Options IV""" try: # Debug if cfg.DEBUG: logger.debug("!stocks.opt.iv %s", ticker) # Check for argument if ticker is None: raise Exception("Stock ticker is required") df = barchart_model.get_options_info(ticker) df = df.fillna("") df = df.set_axis( [ " ", "", ], axis="columns", ) df.set_index(" ", inplace=True) fig = df2img.plot_dataframe( df, fig_size=(600, 1500), col_width=[3, 3], tbl_cells=dict( align="left", height=35, ), template="plotly_dark", font=dict( family="Consolas", size=20, ), paper_bgcolor="rgba(0, 0, 0, 0)", ) imagefile = "opt-iv.png" df2img.save_dataframe(fig=fig, filename=imagefile) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) image = disnake.File(imagefile) title = f"{ticker.upper()} Options: IV" embed = disnake.Embed(title=title, colour=cfg.COLOR) embed.set_image(url=f"attachment://{imagefile}") embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, file=image) except Exception as e: embed = disnake.Embed( title="ERROR Options: IV", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0)
async def historical_command(ctx, signal: str = "", 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 not in so.d_signals_desc: 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.style.use("seaborn") 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 = disnake.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, delete_after=30.0) 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") imagefile = "scr_historical.png" img = Image.open(imagefile) print(img.size) im_bg = Image.open(cfg.IMG_BG) h = img.height + 240 w = img.width + 520 img = img.resize((w, h), Image.ANTIALIAS) x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0]) y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1]) x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0]) y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1]) img = img.convert("RGB") im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2)) im_bg.save(imagefile, "PNG", quality=100) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) 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 = disnake.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 = disnake.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, delete_after=30.0)
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 plt.style.use("seaborn") 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") imagefile = "ta_donchian.png" img = Image.open(imagefile) print(img.size) im_bg = Image.open(cfg.IMG_BG) h = img.height + 240 w = img.width + 520 img = img.resize((w, h), Image.ANTIALIAS) x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0]) y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1]) x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0]) y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1]) img = img.convert("RGB") im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2)) im_bg.save(imagefile, "PNG", quality=100) image = Image.open(imagefile) image = discordbot.helpers.autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) 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 = disnake.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 = disnake.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, delete_after=30.0)
async def financial_command( ctx, preset="template", sort="", limit: int = 5, ascend: bool = False ): """Displays returned results from preset by financial metrics [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.financial %s %s %s %s", preset, sort, limit, ascend ) # Check for argument if limit < 0: raise Exception("Number has to be above 0") # Output Data df_screen = get_screener_data( preset, "financial", 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["financial"]: 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["financial"], 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['financial'])}" ) 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 = [] choices = [ disnake.SelectOption(label="Overview", value="0", emoji="🟢"), ] initial_str = description + "Overview" i = 1 for column in df_screen.columns.values: menu = f"\nPage {i}: {column}" initial_str += f"\nPage {i}: {column}" if i < 19: choices.append( disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"), ) if i == 20: choices.append( disnake.SelectOption( label="Max Reached", value=f"{i}", emoji="🟢" ), ) i += 1 columns.append( disnake.Embed( title="Stocks: [Finviz] Financial 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( disnake.Embed( title="Stocks: [Finviz] Financial Screener", description="```" + df_screen[column].fillna("").to_string() + "```", colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) ) await ctx.send(embed=columns[0], view=Menu(columns, choices)) except Exception as e: embed = disnake.Embed( title="ERROR Stocks: [Finviz] Financial Screener", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0)
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 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 = go.Figure() fig.add_trace( go.Scatter( name=f"{ticker}", x=price_df.index, y=price_df["Price"], line=dict(color="#fdc708", width=2), opacity=1, ), ) for i in range(1, price_df.shape[1]): trace_name = price_df.columns[i].replace("_", " ") fig.add_trace( go.Scatter( name=trace_name, x=price_df.index, y=price_df.iloc[:, i], opacity=1, ), ) fig.update_layout( margin=dict(l=0, r=0, t=50, b=20), template=cfg.PLT_TA_STYLE_TEMPLATE, colorway=cfg.PLT_TA_COLORWAY, title=f"{ticker} WMA", title_x=0.5, yaxis_title="Stock Price ($)", xaxis_title="Time", yaxis=dict(fixedrange=False, ), xaxis=dict( rangeslider=dict(visible=False), type="date", ), dragmode="pan", legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01), ) config = dict({"scrollZoom": True}) imagefile = "ta_wma.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = random.randint(69, 69420) fig.write_html(f"in/wma_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/wma_{html_ran}.html)" fig.update_layout( width=800, height=500, ) fig.write_image(imagefile) img = Image.open(imagefile) print(img.size) im_bg = Image.open(cfg.IMG_BG) h = img.height + 240 w = img.width + 520 # Paste fig onto background img and autocrop background img = img.resize((w, h), Image.ANTIALIAS) x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0]) y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1]) x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0]) y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1]) img = img.convert("RGB") im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2)) im_bg.save(imagefile, "PNG", quality=100) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) image = disnake.File(imagefile) print(f"Image {imagefile}") if cfg.DEBUG: logger.debug("Image: %s", imagefile) title = "Stocks: Weighted-Moving-Average " + ticker embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR) embed.set_image(url=f"attachment://{imagefile}") embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) os.remove(imagefile) await ctx.send(embed=embed, file=image) except Exception as e: embed = disnake.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, delete_after=30.0)
async def meats_command(ctx): """Displays meats futures data [Finviz]""" try: # Debug user input if cfg.DEBUG: logger.debug("econ-meats") # Retrieve data d_futures = finviz_model.get_futures() df = pd.DataFrame(d_futures["Meats"]) # Check for argument if df.empty: raise Exception("No available data found") df = df.fillna("") df = df.set_index("label") df = df.sort_values(by="ticker", ascending=False) formats = {"last": "${:.2f}", "prevClose": "${:.2f}"} for col, value in formats.items(): df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640 # Debug user output if cfg.DEBUG: logger.debug(df) df = df[ [ "prevClose", "last", "change", ] ] df.index.names = [""] df = df.rename( columns={"prevClose": "PrevClose", "last": "Last", "change": "Change"} ) dindex = len(df.index) fig = df2img.plot_dataframe( df, fig_size=(800, (40 + (40 * dindex))), col_width=[6, 3, 3], tbl_cells=dict( align="left", height=35, ), template="plotly_dark", font=dict( family="Consolas", size=20, ), paper_bgcolor="rgba(0, 0, 0, 0)", ) imagefile = "econ-meats.png" df2img.save_dataframe(fig=fig, filename=imagefile) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) image = disnake.File(imagefile) title = "Economy: [Finviz] Meats Futures" embed = disnake.Embed(title=title, colour=cfg.COLOR) embed.set_image(url=f"attachment://{imagefile}") embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) os.remove(imagefile) await ctx.send(embed=embed, file=image) except Exception as e: embed = disnake.Embed( title="ERROR Economy: [Finviz] Meats Futures", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0)
async def feargreed_command(ctx, indicator=""): """CNN Fear and Greed Index [CNN]""" try: # Debug user input if cfg.DEBUG: logger.debug("econ-futures") # Check for argument possible_indicators = ("", "jbd", "mv", "pco", "mm", "sps", "spb", "shd") if indicator not in possible_indicators: raise Exception( f"Select a valid indicator from {', '.join(possible_indicators)}" # nosec ) # Retrieve data fig = plt.figure(figsize=[1, 1], dpi=10) report, _ = cnn_model.get_feargreed_report(indicator, fig) cnn_view.fear_and_greed_index(indicator=indicator, export="png") plt.close("all") # Output data now = datetime.datetime.now() image_path = os.path.join( cfg.GST_PATH, "exports", "economy", f"feargreed_{now.strftime('%Y%m%d_%H%M%S')}.png", ) i = 0 while not os.path.exists(image_path) and i < 10: now -= datetime.timedelta(seconds=1) image_path = os.path.join( cfg.GST_PATH, "exports", "economy", f"feargreed_{now.strftime('%Y%m%d_%H%M%S')}.png", ) i += 1 embed = disnake.Embed( title="Economy: [CNN] Fear Geed Index", description=report, colour=cfg.COLOR, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) if os.path.exists(image_path): uploaded_image = gst_imgur.upload_image( image_path, title="FearGreed Charts" ) embed.set_image(url=uploaded_image.link) else: logger.error("Error when uploading the the image to Imgur.") await ctx.send(embed=embed) except Exception as e: logger.error("ERROR Economy: [CNN] Feargreed. %s", e) embed = disnake.Embed( title="ERROR Economy: [CNN] Feargreed", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0)
async def kc_command(ctx, ticker="", length="20", scalar="2", mamode="sma", offset="0", start="", end=""): """Displays chart with keltner channel [Yahoo Finance]""" try: # Debug if cfg.DEBUG: # pylint: disable=logging-too-many-args logger.debug( "!stocks.ta.kc %s %s %s %s %s %s %s", ticker, length, scalar, mamode, offset, start, end, ) # Check for argument possible_ma = ["sma", "ema", "wma", "hma", "zlma"] 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) if not offset.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") offset = float(offset) if mamode not in possible_ma: raise Exception("Invalid ma entered") 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.kc( df_stock["High"], df_stock["Low"], df_stock["Adj Close"], length, scalar, mamode, offset, ) # Output Data fig = go.Figure() fig.add_trace( go.Scatter( name="upper", x=df_ta.index, y=df_ta.iloc[:, 2].values, opacity=1, mode="lines", line_color="indigo", showlegend=False, ), ) fig.add_trace( go.Scatter( name="lower", x=df_ta.index, y=df_ta.iloc[:, 0].values, opacity=1, mode="lines", line_color="indigo", fill="tonexty", fillcolor="rgba(74, 0, 128, 0.2)", showlegend=False, ), ) fig.add_trace( go.Scatter( name="mid", x=df_ta.index, y=df_ta.iloc[:, 1].values, opacity=1, line=dict( width=1.5, dash="dash", ), ), ) fig.add_trace( go.Scatter( name=f"{ticker}", x=df_stock.index, y=df_stock["Adj Close"], line=dict(color="#fdc708", width=2), opacity=1, ), ) fig.update_layout( margin=dict(l=0, r=0, t=50, b=20), template=cfg.PLT_TA_STYLE_TEMPLATE, colorway=cfg.PLT_TA_COLORWAY, title=f"{ticker} Keltner Channels ({mamode.upper()})", title_x=0.5, yaxis_title="Stock Price ($)", xaxis_title="Time", yaxis=dict(fixedrange=False, ), xaxis=dict( rangeslider=dict(visible=False), type="date", ), dragmode="pan", legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01), ) config = dict({"scrollZoom": True}) imagefile = "ta_kc.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = random.randint(69, 69420) fig.write_html(f"in/kc_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/kc_{html_ran}.html)" fig.update_layout( width=800, height=500, ) fig.write_image(imagefile) img = Image.open(imagefile) print(img.size) im_bg = Image.open(cfg.IMG_BG) h = img.height + 240 w = img.width + 520 # Paste fig onto background img and autocrop background img = img.resize((w, h), Image.ANTIALIAS) x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0]) y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1]) x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0]) y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1]) img = img.convert("RGB") im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2)) im_bg.save(imagefile, "PNG", quality=100) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) image = disnake.File(imagefile) print(f"Image {imagefile}") if cfg.DEBUG: logger.debug("Image: %s", imagefile) title = "Stocks: Keltner-Channel " + ticker embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR) embed.set_image(url=f"attachment://{imagefile}") embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) os.remove(imagefile) await ctx.send(embed=embed, file=image) except Exception as e: embed = disnake.Embed( title="ERROR Stocks: Keltner-Channel", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0)
async def topbuys_command( ctx, gov_type="", past_transactions_months: int = 5, num: int = 20, raw: bool = False, ): """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, ) 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() + "```" plt.style.use("seaborn") 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") imagefile = "gov_topbuys.png" img = Image.open(imagefile) print(img.size) im_bg = Image.open(cfg.IMG_BG) h = img.height + 240 w = img.width + 520 img = img.resize((w, h), Image.ANTIALIAS) x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0]) y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1]) x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0]) y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1]) img = img.convert("RGB") im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2)) im_bg.save(imagefile, "PNG", quality=100) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) 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 = disnake.Embed(title=title, description=description, colour=cfg.COLOR) else: embed = disnake.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 = disnake.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, delete_after=30.0)
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) dmin = df_ta.values.min() dmax = df_ta.values.max() # Output Data fig = make_subplots( rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.07, row_width=[0.5, 0.6], ) fig.add_trace( go.Scatter( name=ticker, x=df_stock.index, y=df_stock["Adj Close"].values, line=dict(color="#fdc708", width=2), opacity=1, showlegend=False, ), row=1, col=1, ) fig.add_trace( go.Scatter( name="Fisher", x=df_ta.index, y=df_ta.iloc[:, 0].values, opacity=1, ), row=2, col=1, ) fig.add_trace( go.Scatter( name="Signal", x=df_ta.index, y=df_ta.iloc[:, 1].values, opacity=1, ), row=2, col=1, ) fig.add_hrect( y0=2, y1=dmax, fillcolor="red", opacity=0.2, layer="below", line_width=0, row=2, col=1, ) fig.add_hrect( y0=-2, y1=dmin, fillcolor="green", opacity=0.2, layer="below", line_width=0, row=2, col=1, ) fig.add_hline( y=-2, fillcolor="green", opacity=1, layer="below", line_width=3, line=dict(color="green", dash="dash"), row=2, col=1, ) fig.add_hline( y=2, fillcolor="red", opacity=1, layer="below", line_width=3, line=dict(color="red", dash="dash"), row=2, col=1, ) fig.update_layout( margin=dict(l=0, r=20, t=30, b=20), template=cfg.PLT_TA_STYLE_TEMPLATE, colorway=cfg.PLT_TA_COLORWAY, title=f"{ticker} Fisher Transform", title_x=0.5, yaxis_title="Stock Price ($)", yaxis=dict(fixedrange=False, ), xaxis=dict( rangeslider=dict(visible=False), type="date", ), dragmode="pan", legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01), ) config = dict({"scrollZoom": True}) imagefile = "ta_fisher.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = random.randint(69, 69420) fig.write_html(f"in/fisher_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/fisher_{html_ran}.html)" fig.update_layout( width=800, height=500, ) fig.write_image(imagefile) img = Image.open(imagefile) print(img.size) im_bg = Image.open(cfg.IMG_BG) h = img.height + 240 w = img.width + 520 # Paste fig onto background img and autocrop background img = img.resize((w, h), Image.ANTIALIAS) x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0]) y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1]) x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0]) y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1]) img = img.convert("RGB") im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2)) im_bg.save(imagefile, "PNG", quality=100) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) image = disnake.File(imagefile) print(f"Image {imagefile}") if cfg.DEBUG: logger.debug("Image: %s", imagefile) title = "Stocks: Fisher-Transform " + ticker embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR) embed.set_image(url=f"attachment://{imagefile}") embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) os.remove(imagefile) await ctx.send(embed=embed, file=image) except Exception as e: embed = disnake.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, delete_after=30.0)
async def chain_command( ctx, ticker: str = None, expiry: str = None, opt_type: str = None, min_sp: float = None, max_sp: float = None, ): """Show calls/puts for given ticker and expiration""" try: # Debug if cfg.DEBUG: logger.debug("opt-chain %s %s %s %s %s", ticker, expiry, opt_type, min_sp, max_sp) # Check for argument if not ticker: 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, str(expiry)) calls_df = options.calls puts_df = options.puts column_map = { "openInterest": "oi", "volume": "vol", "impliedVolatility": "iv" } columns = [ "strike", "bid", "ask", "volume", "openInterest", "impliedVolatility", ] if opt_type == "Calls": df = calls_df[columns].rename(columns=column_map) if opt_type == "Puts": df = puts_df[columns].rename(columns=column_map) min_strike = np.percentile(df["strike"], 1) max_strike = np.percentile(df["strike"], 100) if min_sp: min_strike = min_sp if max_sp: max_strike = max_sp if min_sp > max_sp: # type: ignore min_sp, max_sp = max_strike, min_strike df = df[df["strike"] >= min_strike] df = df[df["strike"] <= max_strike] df["iv"] = pd.to_numeric(df["iv"].astype(float)) formats = {"iv": "{:.2f}"} for col, f in formats.items(): df[col] = df[col].map(lambda x: f.format(x)) # pylint: disable=W0640 df.set_index("strike", inplace=True) title = f"Stocks: {opt_type} Option Chain for {ticker.upper()} on {expiry} [yfinance]" embeds: list = [] # Weekly Calls Pages i, i2, end = 0, 0, 20 df_pg = [] embeds_img = [] dindex = len(df.index) while i < dindex: df_pg = df.iloc[i:end] df_pg.append(df_pg) figp = df2img.plot_dataframe( df_pg, fig_size=(1000, (40 + (40 * 20))), col_width=[3, 3, 3, 3], tbl_cells=dict(height=35, ), font=dict( family="Consolas", size=20, ), template="plotly_dark", paper_bgcolor="rgba(0, 0, 0, 0)", ) imagefile = f"opt-chain{i}.png" df2img.save_dataframe(fig=figp, filename=imagefile) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) uploaded_image = gst_imgur.upload_image(imagefile, title="something") image_link = uploaded_image.link embeds_img.append(f"{image_link}", ) embeds.append(disnake.Embed( title=title, colour=cfg.COLOR, ), ) i2 += 1 i += 20 end += 20 os.remove(imagefile) # Author/Footer for i in range(0, i2): embeds[i].set_author( name=cfg.AUTHOR_NAME, url=cfg.AUTHOR_URL, icon_url=cfg.AUTHOR_ICON_URL, ) embeds[i].set_footer( text=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) i = 0 for i in range(0, i2): embeds[i].set_image(url=embeds_img[i]) i += 1 embeds[0].set_footer(text=f"Page 1 of {len(embeds)}") options = [ disnake.SelectOption(label="Home", value="0", emoji="🟢"), ] await ctx.send(embed=embeds[0], view=Menu(embeds, options)) except Exception as e: embed = disnake.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, delete_after=30.0)
async def indices_command(ctx): """US indices overview [Wall St. Journal]""" try: # Debug user input if cfg.DEBUG: logger.debug("econ-indices") # Retrieve data df = wsj_model.us_indices() # Check for argument if df.empty: raise Exception("No available data found") df["Price"] = pd.to_numeric(df["Price"].astype(float)) df["Chg"] = pd.to_numeric(df["Chg"].astype(float)) df["%Chg"] = pd.to_numeric(df["%Chg"].astype(float)) formats = {"Price": "${:.2f}", "Chg": "${:.2f}", "%Chg": "{:.2f}%"} for col, value in formats.items(): df[col] = df[col].map(lambda x: value.format(x)) # pylint: disable=W0640 # Debug user output if cfg.DEBUG: logger.debug(df) df = df[ [ "Price", "Chg", "%Chg", ] ] df = df.fillna("") df.set_index(" ", inplace=True) dindex = len(df.index) fig = df2img.plot_dataframe( df, fig_size=(800, (40 + (40 * dindex))), col_width=[8, 3, 3], tbl_cells=dict( align=["left", "center"], height=35, ), template="plotly_dark", font=dict( family="Consolas", size=20, ), paper_bgcolor="rgba(0, 0, 0, 0)", ) imagefile = "econ-indices.png" df2img.save_dataframe(fig=fig, filename=imagefile) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) image = disnake.File(imagefile) title = "Economy: [WSJ] US Indices" embed = disnake.Embed(title=title, colour=cfg.COLOR) embed.set_image(url=f"attachment://{imagefile}") embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) os.remove(imagefile) await ctx.send(embed=embed, file=image) except Exception as e: embed = disnake.Embed( title="ERROR Economy: [WSJ] US Indices", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0)
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)] 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 = make_subplots( rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.06, row_width=[0.2, 0.2, 0.2], ) fig.add_trace( go.Scatter( name=ticker, x=df_stock.index, y=df_stock["Adj Close"].values, line=dict(color="#fdc708", width=2), opacity=1, ), row=1, col=1, ) colors = [ "green" if row.Open < row["Adj Close"] else "red" for _, row in df_stock.iterrows() ] fig.add_trace( go.Bar( x=df_stock.index, y=df_vol, name="Volume [M]", marker_color=colors, ), row=2, col=1, ) fig.add_trace( go.Scatter( name="A/D [M]", x=df_ta.index, y=df_ta.iloc[:, 0].values, line=dict(width=2), opacity=1, ), row=3, col=1, ) fig.add_hline( y=0, fillcolor="grey", opacity=1, layer="below", line_width=3, line=dict(color="grey", dash="dash"), row=3, col=1, ) fig.update_layout( margin=dict(l=10, r=0, t=40, b=20), template=cfg.PLT_TA_STYLE_TEMPLATE, colorway=cfg.PLT_TA_COLORWAY, title=f"{ticker} AD", title_x=0.4, yaxis_title="Stock Price ($)", yaxis2_title="Volume [M]", yaxis3_title="A/D [M]", yaxis=dict(fixedrange=False, ), xaxis=dict( rangeslider=dict(visible=False), type="date", ), dragmode="pan", legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1), ) config = dict({"scrollZoom": True}) imagefile = "ta_ad.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = random.randint(69, 69420) fig.write_html(f"in/ad_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/ad_{html_ran}.html)" fig.update_layout( width=800, height=500, ) fig.write_image(imagefile) img = Image.open(imagefile) print(img.size) im_bg = Image.open(cfg.IMG_BG) h = img.height + 240 w = img.width + 520 # Paste fig onto background img and autocrop background img = img.resize((w, h), Image.ANTIALIAS) x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0]) y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1]) x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0]) y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1]) img = img.convert("RGB") im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2)) im_bg.save(imagefile, "PNG", quality=100) image = Image.open(imagefile) image = autocrop_image(image, 0) image.save(imagefile, "PNG", quality=100) image = disnake.File(imagefile) print(f"Image {imagefile}") if cfg.DEBUG: logger.debug("Image: %s", imagefile) title = f"Stocks: Accumulation/Distribution Line {ticker}" embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR) embed.set_image(url=f"attachment://{imagefile}") embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) os.remove(imagefile) await ctx.send(embed=embed, file=image) except Exception as e: embed = disnake.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, delete_after=30.0)
async def lasttrades_command(ctx, gov_type="", past_days: int = 5, 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_days, representative, ) 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())}" ) choices = [ disnake.SelectOption(label="Overview", value="0", emoji="🟢"), ] initial_str = "Overview" i = 1 for col_name in df_gov_rep["Ticker"].values: menu = f"\nPage {i}: {col_name}" initial_str += f"\nPage {i}: {col_name}" if i < 19: choices.append( disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"), ) if i == 20: choices.append( disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"), ) i += 1 columns = [] df_gov_rep = df_gov_rep.T columns.append( disnake.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( disnake.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 ctx.send(embed=columns[0], view=Menu(columns, choices)) else: choices = [ disnake.SelectOption(label="Overview", value="0", emoji="🟢"), ] initial_str = "Overview" i = 1 for col_name in df_gov["Ticker"].values: menu = f"\nPage {i}: {col_name}" initial_str += f"\nPage {i}: {col_name}" if i < 19: choices.append( disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"), ) if i == 20: choices.append( disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"), ) i += 1 columns = [] df_gov = df_gov.T columns.append( disnake.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( disnake.Embed( description="```" + df_gov[column].fillna("").to_string() + "```", colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) await ctx.send(embed=columns[0], view=Menu(columns, choices)) except Exception as e: embed = disnake.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, delete_after=30.0)
async def lastcontracts_command(ctx, past_transactions_days: int = 2, num: int = 20): """Displays last government contracts [quiverquant.com]""" try: # Debug user input if cfg.DEBUG: logger.debug("!stocks.gov.lastcontracts %s %s", past_transactions_days, 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] choices = [ disnake.SelectOption(label="Overview", value="0", emoji="🟢"), ] initial_str = "Overview" i = 1 for col_name in df_contracts["Ticker"].values: menu = f"\nPage {i}: {col_name}" initial_str += f"\nPage {i}: {col_name}" choices.append( disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"), ) i += 1 columns = [] df_contracts = df_contracts.T columns.append( disnake.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( disnake.Embed( description="```" + df_contracts[column].fillna("").to_string() + "```", colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) await ctx.send(embed=columns[0], view=Menu(columns, choices)) except Exception as e: embed = disnake.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, delete_after=30.0)