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 softs_command(ctx): """Displays softs futures data [Finviz]""" try: # Debug user input if cfg.DEBUG: logger.debug("econ-softs") # Retrieve data d_futures = finviz_model.get_futures() df = pd.DataFrame(d_futures["Softs"]) # Check for argument if df.empty: raise Exception("No available data found") 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.sort_values(by="ticker", ascending=False) df = df.fillna("") df.set_index("label", inplace=True) 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-softs.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] Softs 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] Softs 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 hma_command(ctx, ticker="", window="", offset="", start="", end=""): """Displays chart with hull moving average [Yahoo Finance]""" try: # Debug if cfg.DEBUG: logger.debug( "!stocks.ta.hma %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 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: hma_data = overlap_model.hma(values=stock["Adj Close"], length=win, offset=offset) price_df = price_df.join(hma_data) l_legend.append(f"HMA {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} HMA", 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_hma.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = random.randint(69, 69420) fig.write_html(f"in/hma_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/hma_{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: Hull-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: Hull-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 arktrades_command(ctx, ticker: str = "", num: int = 10): """Displays trades made by ark [cathiesark.com]""" try: # Debug user input if cfg.DEBUG: logger.debug("!stocks.dd.arktrades %s", ticker) if ticker: ark_holdings = ark_model.get_ark_trades_by_ticker(ticker) ark_holdings = ark_holdings.drop( columns=["ticker", "everything.profile.companyName"]) if ark_holdings.empty: raise Exception( "Issue getting data from cathiesark.com. Likely no trades found.\n" ) ark_holdings["Total"] = ark_holdings["Total"] / 1_000_000 ark_holdings.rename(columns={ "direction": "B/S", "weight": "F %" }, inplace=True) ark_holdings.index = pd.Series( ark_holdings.index).apply(lambda x: x.strftime("%Y-%m-%d")) df = ark_holdings.head(num) dindex = len(df.head(num).index) formats = {"Close": "{:.2f}", "Total": "{:.2f}"} for col, f in formats.items(): df[col] = df[col].map(lambda x: f.format(x)) # pylint: disable=W0640 title = f"Stocks: [cathiesark.com] {ticker.upper()} Trades by Ark" 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=(900, (40 + (40 * 20))), col_width=[5, 10, 4, 4, 3, 4, 5], 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=f"ERROR Stocks: [cathiesark.com] {ticker} Trades by Ark", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0)
async def unu_command(ctx, num: int = None): """Unusual Options""" try: # Debug if cfg.DEBUG: logger.debug("!stocks.opt.unu %s", num) # Check for argument if num is None: num = 10 pages = np.arange(0, num // 20 + 1) data_list = [] for page_num in pages: r = requests.get( f"https://app.fdscanner.com/api2/unusualvolume?p=0&page_size=20&page={int(page_num)}", headers={"User-Agent": get_user_agent()}, ) if r.status_code != 200: logger.debug("Error in fdscanner request") return pd.DataFrame(), "request error" data_list.append(r.json()) ticker, expiry, option_strike, option_type, ask, bid, oi, vol, voi = ( [], [], [], [], [], [], [], [], [], ) for data in data_list: for entry in data["data"]: ticker.append(entry["tk"]) expiry.append(entry["expiry"]) option_strike.append(float(entry["s"])) option_type.append("Put" if entry["t"] == "P" else "Call") ask.append(entry["a"]) bid.append(entry["b"]) oi.append(entry["oi"]) vol.append(entry["v"]) voi.append(entry["vol/oi"]) df = pd.DataFrame({ "Ticker": ticker, "Exp": expiry, "Strike": option_strike, "Type": option_type, "Vol/OI": voi, "Vol": vol, "OI": oi, }) df = df.replace({"2021-", "2022-"}, "", regex=True) df.set_index("Ticker", inplace=True) dindex = len(df.index) fig = df2img.plot_dataframe( df, fig_size=(800, (40 + (40 * dindex))), col_width=[3, 3, 3, 3, 3, 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-unu.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 = "Unusual Options" 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 Unusual Options", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, 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 adosc_command(ctx, ticker="", is_open="False", fast="3", slow="10", start="", end=""): """Displays chart with chaikin oscillator [Yahoo Finance]""" try: # Debug if cfg.DEBUG: # pylint: disable=logging-too-many-args logger.debug( "!stocks.ta.adosc %s %s %s %s %s", ticker, is_open, fast, slow, start, end, ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) if not fast.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") fast = float(fast) if not slow.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") slow = float(slow) ticker = ticker.upper() df_stock = discordbot.helpers.load(ticker, start) if df_stock.empty: raise Exception("Stock ticker is invalid") # Retrieve Data df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)] # Output Data divisor = 1_000_000 df_vol = df_stock["Volume"].dropna() df_vol = df_vol.values / divisor df_ta = volume_model.adosc(df_stock, is_open, fast, slow) df_cal = df_ta.values df_cal = df_cal / divisor fig = 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", marker_color=colors, ), row=2, col=1, ) fig.add_trace( go.Scatter( name="AD Osc [M]", x=df_ta.index, y=df_ta.iloc[:, 0].values, line=dict(width=2), opacity=1, ), row=3, col=1, ) fig.update_layout( margin=dict(l=10, r=0, t=30, b=20), template=cfg.PLT_TA_STYLE_TEMPLATE, colorway=cfg.PLT_TA_COLORWAY, title=f"{ticker} AD Oscillator", 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_adosc.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = random.randint(69, 69420) fig.write_html(f"in/adosc_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/adosc_{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: Accumulation/Distribution Oscillator " + 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 Oscillator", 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 hist_command( ctx, ticker: str = None, expiry: str = "", strike: float = 10, opt_type: str = "", greek: str = "", ): """Plot historical option prices Parameters ---------- ticker: str Stock ticker expiry: str expiration date strike: float Option strike price put: bool Calls for call Puts for put """ try: # Debug if cfg.DEBUG: print(f"opt-hist {ticker} {strike} {opt_type} {expiry} {greek}") # Check for argument if ticker is None: raise Exception("Stock ticker is required") if opt_type == "Puts": put = bool(True) if opt_type == "Calls": put = bool(False) chain_id = "" df_hist = syncretism_model.get_historical_greeks( ticker, expiry, chain_id, strike, put) title = f"\n{ticker.upper()} {strike} {opt_type} expiring {expiry} Historical {greek.upper()}" # Output data fig = make_subplots(shared_xaxes=True, specs=[[{"secondary_y": True}]]) fig.add_trace( go.Scatter( name=ticker.upper(), x=df_hist.index, y=df_hist.price, line=dict(color="#fdc708", width=2), opacity=1, ), ) fig.add_trace( go.Scatter( name="Premium", x=df_hist.index, y=df_hist["premium"], opacity=1, yaxis="y2", ), ) if greek: fig.add_trace( go.Scatter( name=f"{greek.upper()}", x=df_hist.index, y=df_hist[greek], opacity=1, yaxis="y3", ), ) fig.update_layout( margin=dict(l=10, r=10, t=30, b=20), template=cfg.PLT_TA_STYLE_TEMPLATE, colorway=cfg.PLT_TA_COLORWAY, title=title, title_x=0.03, yaxis_title="<b>Stock Price</b> ($)", yaxis=dict( side="right", fixedrange=False, titlefont=dict(color="#fdc708"), tickfont=dict(color="#fdc708"), position=0.02, nticks=20, ), xaxis=dict( rangeslider=dict(visible=False), type="date", fixedrange=False, domain=[0.037, 1], ), xaxis2=dict( rangeslider=dict(visible=False), type="date", fixedrange=False, ), xaxis3=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", fixedrange=False, anchor="x", overlaying="y", titlefont=dict(color="#d81aea"), tickfont=dict(color="#d81aea"), nticks=20, ), yaxis3=dict( side="left", position=0, fixedrange=False, overlaying="y", titlefont=dict(color="#00e6c3"), tickfont=dict(color="#00e6c3"), nticks=20, ), hovermode="x unified", ) config = dict({"scrollZoom": True}) rand = random.randint(69, 69420) imagefile = f"opt_hist{rand}.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: fig.write_html(f"in/hist_{rand}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/hist_{rand}.html)" fig.update_layout( width=800, height=500, ) fig.write_image(imagefile) img = Image.open(imagefile) 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) if cfg.DEBUG: print(f"Image: {imagefile}") title = f"{ticker.upper()} {strike} {opt_type} expiring {expiry} Historical" 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 Options: History", 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 candle_command( ctx, ticker: str = "", interval: int = 15, past_days: int = 1, start="", end="", ): """Shows candle plot of loaded ticker. [Source: Yahoo Finance, IEX Cloud or Alpha Vantage] Parameters ---------- ticker: str Ticker name interval: int chart mins or daily past_days: int Display the past * days. Default: 1(Not for Daily) start: str start date format YYYY-MM-DD end: str end date format YYYY-MM-DD """ try: logger.info("candle %s %s %s %s", ticker, interval, start, end) 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) futures = "=F" if interval == 1440: df_stock_candidate = yf.download( ticker, start=start, end=end, progress=False, ) # Check that loading a stock was not successful if df_stock_candidate.empty: raise Exception(f"No data found for {ticker.upper()}") df_stock_candidate.index.name = "date" else: s_int = str(interval) + "m" d_granularity = { "1m": past_days, "5m": past_days, "15m": past_days, "30m": past_days, "60m": past_days, } s_start_dt = datetime.utcnow() - timedelta( days=d_granularity[s_int]) s_date_start = s_start_dt.strftime("%Y-%m-%d") df_stock_candidate = yf.download( ticker, start=s_date_start if s_start_dt > start else start.strftime("%Y-%m-%d"), progress=False, interval=s_int, prepost=True, ) # Check that loading a stock was not successful if df_stock_candidate.empty: raise Exception(f"No data found for {ticker.upper()}.") df_stock_candidate.index = df_stock_candidate.index.tz_localize(None) df_stock_candidate.index.name = "date" df_stock = df_stock_candidate price_df = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)] df_vwap = overlap_model.vwap(price_df, 0) plt_title = [f"{ticker.upper()} Intraday {interval}min", "Volume"] title = f"Intraday {interval}min Chart for {ticker.upper()}" if interval == 1440: plt_title = [f"{ticker.upper()} Daily", "Volume"] title = f"Daily Chart for {ticker.upper()}" fig = make_subplots( rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.06, subplot_titles=plt_title, row_width=[0.2, 0.7], ) fig.add_trace( go.Candlestick( x=df_stock.index, open=df_stock.Open, high=df_stock.High, low=df_stock.Low, close=df_stock.Close, name="OHLC", showlegend=False, ), 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_stock.Volume, name="Volume", marker_color=colors, showlegend=False, ), row=2, col=1, ) fig.update_layout( margin=dict(l=0, r=0, t=25, b=20), template=cfg.PLT_CANDLE_STYLE_TEMPLATE, yaxis_title="Stock Price ($)", yaxis=dict( fixedrange=False, showspikes=True, ), xaxis=dict( rangeslider=dict(visible=False), type="date", showspikes=True, ), legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1), dragmode="pan", hovermode="x unified", ) if interval != 1440: fig.add_trace( go.Scatter( name="VWAP", x=df_stock.index, y=df_vwap["VWAP_D"], opacity=1, line=dict(color="#d81aea", width=2), showlegend=True, ), ) if futures in ticker.upper(): fig.update_xaxes(rangebreaks=[ dict(bounds=["fri", "sun"]), dict(bounds=[17, 17.5], pattern="hour"), ], ) else: fig.update_xaxes(rangebreaks=[ dict(bounds=["sat", "mon"]), dict(bounds=[19, 9.5], pattern="hour"), ], ) config = dict({"scrollZoom": True}) rand = random.randint(69, 69420) imagefile = f"candle{rand}.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: fig.write_html(f"in/candle_{rand}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/candle_{rand}.html)" fig.update_layout( width=800, height=500, ) fig.write_image(imagefile) img = Image.open(imagefile) 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}") 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: Intraday", colour=cfg.COLOR, description=e, ) await ctx.send(embed=embed, delete_after=30.0)
async def cc_hist_command( ctx, ticker: str = None, expiry: str = "", strike: float = 10, opt_type: str = "" ): """Plot historical option prices Parameters ---------- ticker: str Stock ticker expiry: str expiration date strike: float Option strike price put: bool Calls for call Puts for put """ try: # Debug if cfg.DEBUG: print(f"opt-hist {ticker} {strike} {opt_type} {expiry}") # Check for argument if ticker is None: raise Exception("Stock ticker is required") yf_ticker = yf.Ticker(ticker) dates = list(yf_ticker.options) if not dates: raise Exception("Stock ticker is invalid") options = yf.Ticker(ticker).option_chain(expiry) if opt_type == "Calls": options = options.calls if opt_type == "Puts": options = options.puts chain_id = options.loc[options.strike == strike, "contractSymbol"].values[0] df_hist = yf.download(chain_id) df_hist.index.name = "date" plt_title = [ f"\n{ticker.upper()} {strike} {opt_type} expiring {expiry} Historical", "Volume", ] title = f"\n{ticker.upper()} {strike} {opt_type} expiring {expiry} Historical" fig = make_subplots( rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.06, subplot_titles=plt_title, row_width=[0.2, 0.7], ) fig.add_trace( go.Candlestick( x=df_hist.index, open=df_hist.Open, high=df_hist.High, low=df_hist.Low, close=df_hist.Close, name="OHLC", ), row=1, col=1, ) colors = [ "green" if row.Open < row["Close"] else "red" for _, row in df_hist.iterrows() # pylint: disable=E1120 ] fig.add_trace( go.Bar( x=df_hist.index, y=df_hist.Volume, name="Volume", marker_color=colors, ), row=2, col=1, ) fig.update_layout( margin=dict(l=0, r=0, t=25, b=5), template=cfg.PLT_CANDLE_STYLE_TEMPLATE, showlegend=False, yaxis_title="Premium", xaxis=dict( rangeslider=dict(visible=False), type="date", ), dragmode="pan", ) fig.update_xaxes( rangebreaks=[ dict(bounds=["sat", "mon"]), ], ) config = dict({"scrollZoom": True}) rand = random.randint(69, 69420) imagefile = f"opt_hist{rand}.png" fig.write_image(imagefile) # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: fig.write_html(f"in/cc_hist_{rand}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/cc_hist_{rand}.html)" img = Image.open(imagefile) 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) if cfg.DEBUG: print(f"Image: {imagefile}") title = f"{ticker.upper()} {strike} {opt_type} expiring {expiry} Historical" 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 Options: History", 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 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 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 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 overview_command( ctx, ticker: str = None, expiry: str = None, min_sp: float = None, max_sp: float = None, ): """Options Overview""" try: # Debug startTime2 = time.time() if cfg.DEBUG: print(f"!stocks.opt.iv {ticker} {expiry} {min_sp} {max_sp}") # Check for argument if ticker is None: raise Exception("Stock ticker is required") # Get options info/dates, Look for logo_url df = get_options_info(ticker) # Barchart Options IV Overview dates = yfinance_model.option_expirations(ticker) # Expiration dates tup = f"{ticker.upper()}" url = yf.Ticker(tup).info["logo_url"] url += "?raw=true" if url else "" if not dates: raise Exception("Stock ticker is invalid") options = yfinance_model.get_option_chain(ticker, str(expiry)) calls = options.calls puts = options.puts current_price = yfinance_model.get_price(ticker) min_strike2 = np.percentile(calls["strike"], 1) max_strike2 = np.percentile(calls["strike"], 100) min_strike = 0.75 * current_price max_strike = 1.95 * current_price if len(calls) > 40: min_strike = 0.75 * current_price max_strike = 1.25 * current_price if min_sp: min_strike = min_sp min_strike2 = min_sp if max_sp: max_strike = max_sp max_strike2 = max_sp if min_sp > max_sp: # type: ignore min_sp, max_sp = max_strike2, min_strike2 call_oi = calls.set_index("strike")["openInterest"] / 1000 put_oi = puts.set_index("strike")["openInterest"] / 1000 df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True) df_opt = df_opt.rename(columns={ "openInterest_x": "OI_call", "openInterest_y": "OI_put" }) max_pain = op_helpers.calculate_max_pain(df_opt) fig = go.Figure() dmax = df_opt[["OI_call", "OI_put"]].values.max() dmin = df_opt[["OI_call", "OI_put"]].values.min() fig.add_trace( go.Scatter( x=df_opt.index, y=df_opt["OI_call"], name="Calls", mode="lines+markers", line=dict(color="green", width=3), )) fig.add_trace( go.Scatter( x=df_opt.index, y=df_opt["OI_put"], name="Puts", mode="lines+markers", line=dict(color="red", width=3), )) fig.add_trace( go.Scatter( x=[current_price, current_price], y=[dmin, dmax], mode="lines", line=dict(color="gold", width=2), name="Current Price", )) fig.add_trace( go.Scatter( x=[max_pain, max_pain], y=[dmin, dmax], mode="lines", line=dict(color="grey", width=3, dash="dash"), name=f"Max Pain: {max_pain}", )) fig.update_xaxes( range=[min_strike, max_strike], constrain="domain", ) fig.update_layout( margin=dict(l=0, r=0, t=60, b=20), template=cfg.PLT_SCAT_STYLE_TEMPLATE, title=f"Open Interest for {ticker.upper()} expiring {expiry}", title_x=0.5, legend_title="", xaxis_title="Strike", yaxis_title="Open Interest (1k)", xaxis=dict(rangeslider=dict(visible=False), ), legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01), dragmode="pan", ) config = dict({"scrollZoom": True}) imagefile = "opt_oi.png" fig.write_image(imagefile) plt_link = "" if cfg.INTERACTIVE: html_ran = random.randint(69, 69420) fig.write_html(f"in/oi_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/oi_{html_ran}.html)" img = Image.open(imagefile) 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_oi = gst_imgur.upload_image(imagefile, title="something") image_link_oi = uploaded_image_oi.link column_map = { "openInterest": "oi", "volume": "vol", "impliedVolatility": "iv" } columns = [ "strike", "bid", "ask", "volume", "openInterest", "impliedVolatility", ] calls_df = calls[columns].rename(columns=column_map) puts_df = puts[columns].rename(columns=column_map) calls_df = calls_df[calls_df["strike"] >= min_strike2] calls_df = calls_df[calls_df["strike"] <= max_strike2] puts_df = puts_df[puts_df["strike"] >= min_strike2] puts_df = puts_df[puts_df["strike"] <= max_strike2] calls_df["iv"] = pd.to_numeric(calls_df["iv"].astype(float)) puts_df["iv"] = pd.to_numeric(puts_df["iv"].astype(float)) formats = {"iv": "{:.2f}"} for col, f in formats.items(): calls_df[col] = calls_df[col].map(lambda x: f.format(x) # pylint: disable=W0640 ) puts_df[col] = puts_df[col].map(lambda x: f.format(x) # pylint: disable=W0640 ) calls_df.set_index("strike", inplace=True) puts_df.set_index("strike", inplace=True) if "-" in df.iloc[0, 1]: iv = f"```diff\n- {df.iloc[0, 1]}\n```" else: iv = f"```yaml\n {df.iloc[0, 1]}\n```" pfix, sfix = f"{ticker.upper()} ", f" expiring {expiry}" if expiry == dates[0]: pfix = f"{ticker.upper()} Weekly " sfix = "" embeds = [ disnake.Embed( title=f"{ticker.upper()} Overview", color=cfg.COLOR, ), disnake.Embed( title=f"{pfix}Open Interest{sfix}", description=plt_link, colour=cfg.COLOR, ), ] choices = [ disnake.SelectOption(label=f"{ticker.upper()} Overview", value="0", emoji="🟢"), disnake.SelectOption(label=f"{pfix}Open Interest{sfix}", value="1", emoji="🟢"), ] embeds_img = [] i, i2, end = 0, 0, 20 df_calls = [] dindex = len(calls_df.index) while i <= dindex: df_calls = calls_df.iloc[i:end] df_calls.append(df_calls) figp = df2img.plot_dataframe( df_calls, fig_size=(1000, (40 + (40 * 20))), col_width=[3, 3, 3, 3], tbl_cells=cfg.PLT_TBL_CELLS, font=cfg.PLT_TBL_FONT, template=cfg.PLT_TBL_STYLE_TEMPLATE, paper_bgcolor="rgba(0, 0, 0, 0)", ) imagefile = f"opt-calls{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=f"{pfix}Calls{sfix}", colour=cfg.COLOR, ), ) i2 += 1 i += 20 end += 20 os.remove(imagefile) # Add Calls page field i, page, puts_page = 2, 0, 3 i3 = i2 + 2 choices.append( disnake.SelectOption(label="Calls Page 1", value="2", emoji="🟢"), ) for i in range(2, i3): page += 1 puts_page += 1 embeds[i].add_field(name=f"Calls Page {page}", value="_ _", inline=True) # Puts Pages i, end = 0, 20 df_puts = [] dindex = len(puts_df.index) while i <= dindex: df_puts = puts_df.iloc[i:end] df_puts.append(df_puts) figp = df2img.plot_dataframe( df_puts, fig_size=(1000, (40 + (40 * 20))), col_width=[3, 3, 3, 3], tbl_cells=cfg.PLT_TBL_CELLS, font=cfg.PLT_TBL_FONT, template=cfg.PLT_TBL_STYLE_TEMPLATE, paper_bgcolor="rgba(0, 0, 0, 0)", ) imagefile = f"opt-puts{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=f"{pfix}Puts{sfix}", colour=cfg.COLOR, ), ) i2 += 1 i += 20 end += 20 os.remove(imagefile) # Add Puts page field i, page = 0, 0 puts_page -= 1 i2 += 2 choices.append( disnake.SelectOption(label="Puts Page 1", value=f"{puts_page}", emoji="🟢"), ) for i in range(puts_page, i2): page += 1 embeds[i].add_field(name=f"Puts Page {page}", value="_ _", inline=True) # 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, ) # Set images to Pages i = 0 img_i = 0 embeds[1].set_image(url=image_link_oi) for i in range(2, i2): embeds[i].set_image(url=embeds_img[img_i]) img_i += 1 i += 1 if url: embeds[0].set_thumbnail(url=f"{url}") else: embeds[0].set_thumbnail(url=cfg.AUTHOR_ICON_URL) # Overview Section embeds[0].add_field(name=f"{df.iloc[0, 0]}", value=iv, inline=False) embeds[0].add_field(name=f"•{df.iloc[1, 0]}", value=f"```css\n{df.iloc[1, 1]}\n```", inline=True) for N in range(2, 6): embeds[0].add_field( name=f"_ _ _ _ _ _ _ _ _ _ •{df.iloc[N, 0]}", value=f"```css\n{df.iloc[N, 1]}\n```", inline=True, ) embeds[0].add_field(name="_ _", value="_ _", inline=False) for N in range(6, 8): embeds[0].add_field( name=f"_ _ _ _ _ _ _ _ _ _ •{df.iloc[N, 0]}", value=f"```css\n{df.iloc[N, 1]}\n```", inline=True, ) embeds[0].add_field(name="_ _", value="_ _", inline=False) for N in range(8, 10): embeds[0].add_field( name=f"_ _ _ _ _ _ _ _ _ _ •{df.iloc[N, 0]}", value=f"```css\n{df.iloc[N, 1]}\n```", inline=True, ) embeds[0].add_field(name="_ _", value="_ _", inline=False) for N in range(10, 12): embeds[0].add_field( name=f"_ _ _ _ _ _ _ _ _ _ •{df.iloc[N, 0]}", value=f"```css\n{df.iloc[N, 1]}\n```", inline=True, ) embeds[0].set_footer(text=f"Page 1 of {len(embeds)}") executionTime2 = time.time() - startTime2 print( f"\n> {__name__} is finished: time in seconds: {executionTime2}\n") await ctx.send(embed=embeds[0], view=Menu(embeds, choices)) except Exception as e: embed = disnake.Embed( title="ERROR Options: Overview", 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 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 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 valuation_command(ctx, economy_group="sector"): """Valuation of sectors, industry, country [Finviz]""" d_economy_group = { "sector": "Sector", "industry": "Industry", "basic_materials": "Industry (Basic Materials)", "communication_services": "Industry (Communication Services)", "consumer_cyclical": "Industry (Consumer Cyclical)", "consumer_defensive": "Industry (Consumer Defensive)", "energy": "Industry (Energy)", "financial": "Industry (Financial)", "healthcare": "Industry (Healthcare)", "industrials": "Industry (Industrials)", "real_estate": "Industry (Real Estate)", "technology": "Industry (Technology)", "utilities": "Industry (Utilities)", "country": "Country (U.S. listed stocks only)", "capitalization": "Capitalization", } try: # Debug user input if cfg.DEBUG: logger.debug("econ-valuation %s", economy_group) # Select default group if economy_group == "": if cfg.DEBUG: logger.debug("Use default economy_group: 'sector'") economy_group = "sector" # Check for argument possible_groups = list(d_economy_group.keys()) if economy_group not in possible_groups: raise Exception( f"Select a valid group from {', '.join(possible_groups)}" # nosec ) group = d_economy_group[economy_group] # Retrieve data df_group = finviz_model.get_valuation_performance_data( group, "valuation") # 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 = df.set_axis( [ "Name", "MarketCap", "P/E", "FwdP/E", "PEG", "P/S", "P/B", "P/C", "P/FCF", "EPSpast5Y", "EPSnext5Y", "Salespast5Y", "Change", "Volume", ], axis="columns", ) df["P/E"] = pd.to_numeric(df["P/E"].astype(float)) df["FwdP/E"] = pd.to_numeric(df["FwdP/E"].astype(float)) df["EPSpast5Y"] = pd.to_numeric(df["EPSpast5Y"].astype(float)) df["EPSnext5Y"] = pd.to_numeric(df["EPSnext5Y"].astype(float)) df["Salespast5Y"] = pd.to_numeric(df["Salespast5Y"].astype(float)) df["Volume"] = pd.to_numeric(df["Volume"].astype(float)) df["Volume"] = df["Volume"] / 1_000_000 formats = { "P/E": "{:.2f}", "FwdP/E": "{:.2f}", "EPSpast5Y": "{:.2f}", "EPSnext5Y": "{:.2f}", "Salespast5Y": "{:.2f}", "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.fillna("") df.set_index("Name", inplace=True) dindex = len(df.index) fig = df2img.plot_dataframe( df, fig_size=(1600, (40 + (50 * dindex))), col_width=[12, 5, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 4, 4], 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-valuation.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] Valuation {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] Valuation", 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 vol_command( ctx, ticker: str = None, expiry: str = "", min_sp: float = None, max_sp: float = None, ): """Options VOL""" try: # Debug if cfg.DEBUG: print(f"opt-vol {ticker} {expiry} {min_sp} {max_sp}") # Check for argument if ticker is None: raise Exception("Stock ticker is required") dates = yfinance_model.option_expirations(ticker) if not dates: raise Exception("Stock ticker is invalid") options = yfinance_model.get_option_chain(ticker, expiry) current_price = yfinance_model.get_price(ticker) if min_sp is None: min_strike = 0.75 * current_price else: min_strike = min_sp if max_sp is None: max_strike = 1.25 * current_price else: max_strike = max_sp calls = options.calls puts = options.puts call_v = calls.set_index("strike")["volume"] / 1000 put_v = puts.set_index("strike")["volume"] / 1000 df_opt = pd.merge(put_v, call_v, left_index=True, right_index=True) dmax = df_opt.values.max() fig = go.Figure() fig.add_trace( go.Scatter( x=call_v.index, y=call_v.values, name="Calls", mode="lines+markers", line=dict(color="green", width=3), )) fig.add_trace( go.Scatter( x=put_v.index, y=put_v.values, name="Puts", mode="lines+markers", line=dict(color="red", width=3), )) fig.add_trace( go.Scatter( x=[current_price, current_price], y=[0, dmax], mode="lines", line=dict(color="gold", width=2), name="Current Price", )) fig.update_xaxes( range=[min_strike, max_strike], constrain="domain", ) fig.update_layout( margin=dict(l=0, r=0, t=60, b=20), template=cfg.PLT_SCAT_STYLE_TEMPLATE, title=f"Volume for {ticker.upper()} expiring {expiry}", title_x=0.5, legend_title="", xaxis_title="Strike", yaxis_title="Volume (1k)", xaxis=dict(rangeslider=dict(visible=False), ), legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01), dragmode="pan", ) config = dict({"scrollZoom": True}) imagefile = "opt_vol.png" fig.write_image(imagefile) # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = random.randint(69, 69420) fig.write_html(f"in/vol_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/vol_{html_ran}.html)" img = Image.open(imagefile) 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 = discord.File(imagefile) if cfg.DEBUG: print(f"Image: {imagefile}") title = f"Volume for {ticker.upper()} expiring {expiry}" embed = discord.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 = discord.Embed( title="ERROR Options: Volume", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0)
async def vsurf_command( ctx, ticker: str = "", z: str = "IV", ): """Display vol surface Parameters ---------- ticker: Stock Ticker z : The variable for the Z axis """ try: # Debug print(f"!stocks.opt.oi {ticker} {z}") # Check for argument if ticker == "": raise Exception("Stock ticker is required") data = yfinance_model.get_iv_surface(ticker) if data.empty: raise Exception(f"No options data found for {ticker}.\n") Y = data.dte X = data.strike if z == "IV": Z = data.impliedVolatility label = "Volatility" elif z == "OI": Z = data.openInterest label = "Open Interest" elif z == "LP": Z = data.lastPrice label = "Last Price" points3D = np.vstack((X, Y, Z)).T points2D = points3D[:, :2] tri = Delaunay(points2D) I, J, K = tri.simplices.T lighting_effects = dict(ambient=0.5, diffuse=0.5, roughness=0.5, specular=0.4, fresnel=0.4) fig = go.Figure(data=[ go.Mesh3d( z=Z, x=X, y=Y, i=I, j=J, k=K, intensity=Z, colorscale=cfg.PLT_3DMESH_COLORSCALE, hovertemplate="<b>DTE</b>: %{y} <br><b>Strike</b>: %{x} <br><b>" + z + "</b>: %{z}<extra></extra>", showscale=False, flatshading=True, lighting=lighting_effects, ) ]) fig.update_layout(scene=dict( xaxis=dict( title="Strike", tickfont=dict(size=11), titlefont=dict(size=12), ), yaxis=dict(title="DTE", ), zaxis=dict(title=z, ), ), ) fig.update_layout( margin=dict(l=0, r=0, t=40, b=20), width=1320, height=740, template=cfg.PLT_3DMESH_STYLE_TEMPLATE, title=f"{label} Surface for {ticker.upper()}", title_x=0.5, hoverlabel=cfg.PLT_3DMESH_HOVERLABEL, scene_camera=dict( up=dict(x=0, y=0, z=2), center=dict(x=0, y=0, z=-0.3), eye=dict(x=1.25, y=1.25, z=0.69), ), scene=cfg.PLT_3DMESH_SCENE, ) config = dict({"scrollZoom": True}) imagefile = "opt-vsurf.png" fig.write_image(imagefile) # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = random.randint(69, 69420) fig.write_html(f"in/vsurf_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/vsurf_{html_ran}.html)" img = Image.open(imagefile) im_bg = Image.open(cfg.IMG_BG) h = img.height w = img.width # 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}") title = f"{label} Surface for {ticker.upper()}" 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 IndexError: embed = disnake.Embed( title="ERROR Options: Volatility Surface", colour=cfg.COLOR, description=f"Inconistent data for {ticker.upper()} {z}", ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0) except Exception as e: embed = disnake.Embed( title="ERROR Options: Volatility Surface", 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 oi_command( ctx, ticker: str = None, expiry: str = "", min_sp: float = None, max_sp: float = None, ): """Options OI""" try: # Debug print(f"!stocks.opt.oi {ticker} {expiry} {min_sp} {max_sp}") # Check for argument if ticker is None: raise Exception("Stock ticker is required") dates = yfinance_model.option_expirations(ticker) if not dates: raise Exception("Stock ticker is invalid") options = yfinance_model.get_option_chain(ticker, expiry) calls = options.calls puts = options.puts current_price = yfinance_model.get_price(ticker) min_strike = 0.75 * current_price max_strike = 1.95 * current_price if len(calls) > 40: min_strike = 0.75 * current_price max_strike = 1.25 * current_price if min_sp: min_strike = min_sp if max_sp: max_strike = max_sp call_oi = calls.set_index("strike")["openInterest"] / 1000 put_oi = puts.set_index("strike")["openInterest"] / 1000 df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True) df_opt = df_opt.rename( columns={"openInterest_x": "OI_call", "openInterest_y": "OI_put"} ) max_pain = op_helpers.calculate_max_pain(df_opt) fig = go.Figure() dmax = df_opt[["OI_call", "OI_put"]].values.max() dmin = df_opt[["OI_call", "OI_put"]].values.min() fig.add_trace( go.Scatter( x=df_opt.index, y=df_opt["OI_call"], name="Calls", mode="lines+markers", line=dict(color="green", width=3), ) ) fig.add_trace( go.Scatter( x=df_opt.index, y=df_opt["OI_put"], name="Puts", mode="lines+markers", line=dict(color="red", width=3), ) ) fig.add_trace( go.Scatter( x=[current_price, current_price], y=[dmin, dmax], mode="lines", line=dict(color="gold", width=2), name="Current Price", ) ) fig.add_trace( go.Scatter( x=[max_pain, max_pain], y=[dmin, dmax], mode="lines", line=dict(color="grey", width=3, dash="dash"), name=f"Max Pain: {max_pain}", ) ) fig.update_xaxes( range=[min_strike, max_strike], constrain="domain", ) fig.update_layout( margin=dict(l=0, r=0, t=60, b=20), template=cfg.PLT_SCAT_STYLE_TEMPLATE, title=f"Open Interest for {ticker.upper()} expiring {expiry}", title_x=0.5, legend_title="", xaxis_title="Strike", yaxis_title="Open Interest (1k)", xaxis=dict( rangeslider=dict(visible=False), ), legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01), dragmode="pan", ) config = dict({"scrollZoom": True}) imagefile = "opt_oi.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = random.randint(69, 69420) fig.write_html(f"in/cci_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/cci_{html_ran}.html)" fig.update_layout( width=800, height=500, ) fig.write_image(imagefile) img = Image.open(imagefile) 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) image = disnake.File(imagefile) if cfg.DEBUG: print(f"Image URL: {imagefile}") title = f"Open Interest for {ticker.upper()} expiring {expiry}" 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 Options: Open 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 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 rsi_command(ctx, ticker="", length="14", scalar="100", drift="1", start="", end=""): """Displays chart with relative strength index [Yahoo Finance]""" try: # Debug if cfg.DEBUG: # pylint: disable=logging-too-many-args logger.debug( "!stocks.ta.rsi %s %s %s %s %s %s", ticker, length, scalar, drift, start, end, ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) if not length.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") length = float(length) if not scalar.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") scalar = float(scalar) if not drift.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") drift = float(drift) ticker = ticker.upper() df_stock = discordbot.helpers.load(ticker, start) if df_stock.empty: raise Exception("Stock ticker is invalid") # Retrieve Data df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)] df_ta = momentum_model.rsi(df_stock["Adj Close"], length, scalar, drift) # Output Data fig = 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, ) for i in range(0, df_ta.shape[1]): trace_name = df_ta.columns[i].replace("_", " ") fig.add_trace( go.Scatter( name=trace_name, x=df_ta.index, y=df_ta.iloc[:, i], opacity=1, ), row=2, col=1, ) fig.add_hrect( y0=70, y1=100, fillcolor="red", opacity=0.2, layer="below", line_width=0, row=2, col=1, ) fig.add_hrect( y0=0, y1=30, fillcolor="green", opacity=0.2, layer="below", line_width=0, row=2, col=1, ) fig.add_hline( y=70, fillcolor="green", opacity=1, layer="below", line_width=3, line=dict(color="red", dash="dash"), row=2, col=1, ) fig.add_hline( y=30, fillcolor="green", opacity=1, layer="below", line_width=3, line=dict(color="green", 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} {trace_name}", 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_rsi.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = random.randint(69, 69420) fig.write_html(f"in/rsi_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/rsi_{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: Relative-Strength-Index {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: Relative-Strength-Index", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, 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 futures_command(ctx): """Futures and commodities overview [Wall St. Journal]""" try: # Debug user input if cfg.DEBUG: logger.debug("econ-futures") # Retrieve data df = wsj_model.top_commodities() # 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)) # Debug user output if cfg.DEBUG: logger.debug(df.to_string()) 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 df = df.fillna("") df.set_index(" ", inplace=True) df = df[[ "Price", "Chg", "%Chg", ]] 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", height=35, ), template="plotly_dark", font=dict( family="Consolas", size=20, ), paper_bgcolor="rgba(0, 0, 0, 0)", ) imagefile = "econ-futures.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] Futures/Commodities" 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] Futures/Commodities", 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)