def customer_command(ticker: str = ""): """Displays customers of the company [CSIMarket]""" # Debug user input if cfg.DEBUG: logger.debug("dd-customer %s", ticker) if not ticker: raise Exception("A ticker is required") tickers = csimarket_model.get_customers(ticker) if not tickers: raise Exception("Enter a valid ticker") # Debug user output if cfg.DEBUG: logger.debug(tickers) # Output data return { "title": f"Stocks: [CSIMarket] {ticker} Customers", "description": tickers, }
def metals_command(): """Displays metals futures data [Finviz]""" # Debug user input if cfg.DEBUG: logger.debug("econ-metals") # Retrieve data d_futures = finviz_model.get_futures() df = pd.DataFrame(d_futures["Metals"]) # 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 = save_image("econ-metals.png", fig) return { "title": "Economy: [WSJ] Metals Futures", "imagefile": imagefile, }
def toplobbying_command(num: int = 10, raw: bool = False): """Displays top lobbying firms [quiverquant.com]""" # Debug user input if cfg.DEBUG: logger.debug("gov-toplobbying %s %s", num, raw) # Retrieve Data df_lobbying = quiverquant_model.get_government_trading( "corporate-lobbying") if df_lobbying.empty: raise Exception("No corporate lobbying found") df_lobbying["Amount"] = df_lobbying.Amount.astype(float).fillna( 0) / 100_000 lobbying_by_ticker = pd.DataFrame( df_lobbying.groupby("Ticker")["Amount"].agg("sum")).sort_values( by="Amount", ascending=False) fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) lobbying_by_ticker.head(num).plot(kind="bar", ax=ax) ax.set_xlabel("Ticker") ax.set_ylabel("Total Amount ($100k)") ax.set_title(f"Corporate Lobbying Spent since {df_lobbying['Date'].min()}") fig.tight_layout() plt.savefig("ta_toplobbying.png") imagefile = "ta_toplobbying.png" imagefile = image_border(imagefile) return { "title": "Stocks: [quiverquant.com] Top Lobbying Firms", "imagefile": imagefile, }
def quote_command(ticker: str = None): """Ticker Quote""" # Debug if cfg.DEBUG: logger.debug("quote %s", ticker) # Check for argument if ticker is None: raise Exception("Stock ticker is required") df = quote(ticker) fig = df2img.plot_dataframe( df, fig_size=(600, 1500), col_width=[2, 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 = save_image("quote.png", fig) return { "title": f"{ticker.upper()} Quote", "imagefile": imagefile, }
def usbonds_command(): """US bonds overview [Wall St. Journal]""" # Debug user input if cfg.DEBUG: logger.debug("econ-usbonds") # Retrieve data df = wsj_model.us_bonds() # Check for argument if df.empty: raise Exception("No available data found") df["Rate (%)"] = pd.to_numeric(df["Rate (%)"].astype(float)) df["Yld (%)"] = pd.to_numeric(df["Yld (%)"].astype(float)) df["Yld Chg (%)"] = pd.to_numeric(df["Yld Chg (%)"].astype(float)) formats = { "Rate (%)": "{:.2f}%", "Yld (%)": "{:.2f}%", "Yld 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.set_axis( [ "Rate", "Yld", "Yld Chg", ], axis="columns", ) 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 = save_image("econ-usbonds.png", fig) return { "title": "Economy: [WSJ] US Bonds", "imagefile": imagefile, }
def sec_command(ticker=""): """Displays sec filings [Market Watch]""" # Debug user input if cfg.DEBUG: logger.debug("dd-sec %s", ticker) if ticker == "": raise Exception("A ticker is required") df_financials = marketwatch_model.get_sec_filings(ticker) if df_financials.empty: raise Exception("Enter a valid ticker") # Debug user output if cfg.DEBUG: logger.debug(df_financials.to_string()) df = df_financials df.loc[:, "Link"] = "[Link Source](" + df.loc[:, "Link"].astype(str) df.loc[:, "Link"] = df.loc[:, "Link"] + ")" # Output data return { "title": "Stocks: [Market Watch] SEC Filings", "description": df.to_string(), }
def currencies_command(): """Currencies overview [Wall St. Journal]""" # Debug user input if cfg.DEBUG: logger.debug("econ-currencies") # Retrieve data df = wsj_model.global_currencies() df = pd.DataFrame.from_dict(df) # Check for argument if df.empty: raise Exception("No available data found") df["Last"] = pd.to_numeric(df["Last"].astype(float)) df["Chng"] = pd.to_numeric(df["Chng"].astype(float)) df["%Chng"] = pd.to_numeric(df["%Chng"].astype(float)) formats = {"Last": "{:.2f}", "Chng": "{:.2f}", "%Chng": "{:.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) # Debug user output if cfg.DEBUG: logger.debug(df.to_string()) df = df[[ "Last", "Chng", "%Chng", ]] 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 = save_image("econ-currencies.png", fig) return { "title": "Economy: [WSJ] Currencies", "imagefile": imagefile, }
def histcont_command(ticker=""): """Displays historical quarterly-contracts [quiverquant.com]""" # Debug user input if cfg.DEBUG: logger.debug("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") raise Exception("No quarterly government contracts found") # Output Data amounts = df_contracts.sort_values(by=["Year", "Qtr"])["Amount"].values qtr = df_contracts.sort_values(by=["Year", "Qtr"])["Qtr"].values year = df_contracts.sort_values(by=["Year", "Qtr"])["Year"].values quarter_ticks = [ f"{quarter[0]}" if quarter[1] == 1 else "" for quarter in zip(year, qtr) ] fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) ax.plot(np.arange(0, len(amounts)), amounts / 1000, "-*", lw=2, ms=15) ax.set_xlim([-0.5, len(amounts) - 0.5]) ax.set_xticks(np.arange(0, len(amounts))) ax.set_xticklabels(quarter_ticks) ax.grid() ax.set_title(f"Historical Quarterly Government Contracts for {ticker.upper()}") ax.set_xlabel("Date") ax.set_ylabel("Amount ($1k)") fig.tight_layout() plt.savefig("gov_histcont.png") imagefile = "gov_histcont.png" imagefile = image_border(imagefile) return { "title": "Stocks: Historical Quarterly Government Contract ", "imagefile": imagefile, }
def presets_default_command(): """Displays default presets""" # Debug if cfg.DEBUG: logger.debug("scr-presets") description = "" for signame, sigdesc in so.d_signals_desc.items(): description += f"**{signame}:** *{sigdesc}*\n" return { "title": "Stocks: Screener Default Presets", "description": description, }
def feargreed_command(indicator=""): """CNN Fear and Greed Index [CNN]""" # Debug user input if cfg.DEBUG: logger.debug("econ-futures") # Check for argument possible_indicators = ("", "jbd", "mv", "pco", "mm", "sps", "spb", "shd") if indicator not in possible_indicators: raise Exception( f"Select a valid indicator from {', '.join(possible_indicators)}" # nosec ) # Retrieve data fig = plt.figure(figsize=[1, 1], dpi=10) report, _ = cnn_model.get_feargreed_report(indicator, fig) cnn_view.fear_and_greed_index(indicator=indicator, export="png") plt.close("all") # Output data now = datetime.datetime.now() image_path = os.path.join( cfg.GST_PATH, "exports", "economy", f"feargreed_{now.strftime('%Y%m%d_%H%M%S')}.png", ) i = 0 while not os.path.exists(image_path) and i < 10: now -= datetime.timedelta(seconds=1) image_path = os.path.join( cfg.GST_PATH, "exports", "economy", f"feargreed_{now.strftime('%Y%m%d_%H%M%S')}.png", ) i += 1 return { "title": "Economy: [CNN] Fear Geed Index", "imagefile": image_path, "description": report, }
def contracts_command( ticker: str = "", past_transaction_days: Union[int, str] = 10, raw: bool = False ): """Displays contracts associated with tickers [quiverquant.com]""" past_transaction_days = int(past_transaction_days) # Debug user input if cfg.DEBUG: logger.debug("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: return { "title": f"Stocks: [quiverquant.com] Contracts by {ticker}", "description": f"{ticker} does not have any contracts", } # Output Data df_contracts["Date"] = pd.to_datetime(df_contracts["Date"]).dt.date df_contracts = df_contracts[ df_contracts["Date"].isin(df_contracts["Date"].unique()[:past_transaction_days]) ] df_contracts.drop_duplicates(inplace=True) fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) df_contracts.groupby("Date").sum().div(1000).plot(kind="bar", rot=0, ax=ax) ax.set_ylabel("Amount ($1k)") ax.set_title(f"Sum of latest government contracts to {ticker}") fig.tight_layout() plt.savefig("gov_contracts.png") imagefile = "gov_contracts.png" imagefile = image_border(imagefile) return { "title": f"Stocks: [quiverquant.com] Contracts by {ticker}", "imagefile": imagefile, }
def analyst_command(ticker=""): """Displays analyst recommendations [Finviz]""" # Debug if cfg.DEBUG: logger.debug("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 = save_image("dd-analyst.png", fig) return { "title": f"Stocks: [Finviz] Analyst Recommendations {ticker.upper()}", "imagefile": imagefile, }
def summary_command(ticker=""): """Displays text of a given stocks ta summary [FinBrain API]""" # Debug if cfg.DEBUG: logger.debug("ta-summary %s", ticker) # Check for argument if ticker == "": raise Exception("Stock ticker is required") report = finbrain_model.get_technical_summary_report(ticker) if report: report = "```" + report.replace(". ", ".\n") + "```" return { "title": "Stocks: [FinBrain API] Summary", "description": report, }
def iv_command(ticker: str = None): """Options IV""" # Debug if cfg.DEBUG: logger.debug("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 = save_image("opt-iv.png", fig) return { "title": f"{ticker.upper()} Options: IV", "imagefile": imagefile, }
def ford_command(): """Display Orders by Fidelity Customers. [Source: Fidelity]""" # Debug if cfg.DEBUG: logger.debug("disc-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 = save_image("disc-ford.png", fig) return { "title": "Fidelity Customer Orders", "imagefile": imagefile, }
def recom_command(ticker=""): """Displays text of a given stocks recommendation based on ta [Tradingview API]""" # Debug if cfg.DEBUG: logger.debug("ta-recom %s", ticker) # Check for argument if ticker == "": raise Exception("Stock ticker is required") recom = tradingview_model.get_tradingview_recommendation( ticker, "america", "", "") recom = recom[["BUY", "NEUTRAL", "SELL", "RECOMMENDATION"]] report = "```" + recom.to_string() + "```" return { "title": "Stocks: [Tradingview API] Recommendation based on TA", "description": report, }
def presets_custom_command(): """Displays every custom preset""" # Debug if cfg.DEBUG: logger.debug("scr-presets") description = "" for preset in so.presets: with open( so.presets_path + preset + ".ini", encoding="utf8", ) as f: preset_line = "" for line in f: if line.strip() == "[General]": break preset_line += line.strip() description += f"**{preset}:** *{preset_line.split('Description: ')[1].replace('#', '')}*\n" return { "title": "Stocks: Screener Custom Presets", "description": description, }
def view_command(ticker=""): """Displays image from Finviz [Finviz]""" # Debug if cfg.DEBUG: logger.debug("ta-view %s", ticker) # Check for argument if ticker == "": raise Exception("Stock ticker is required") image_data = finviz_model.get_finviz_image(ticker) dataBytesIO = io.BytesIO(image_data) im = Image.open(dataBytesIO) im = im.resize((800, 340), Image.ANTIALIAS) imagefile = "ta_view.png" im.save(imagefile, "PNG", quality=100) imagefile = image_border(imagefile) return { "title": f"Stocks: [Finviz] Trendlines & Data {ticker}", "imagefile": imagefile, }
def sma_command(ticker="", window="", offset="", start="", end=""): """Displays chart with simple moving average [Yahoo Finance]""" # Debug if cfg.DEBUG: logger.debug( "ta-sma %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 = 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: sma_data = overlap_model.sma(values=stock["Adj Close"], length=win, offset=offset) price_df = price_df.join(sma_data) l_legend.append(f"SMA {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} SMA", 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_sma.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = helpers.uuid_get() fig.write_html(f"in/sma_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/sma_{html_ran}.html)" fig.update_layout( width=800, height=500, ) imagefile = helpers.image_border(imagefile, fig=fig) return { "title": f"Stocks: Simple-Moving-Average {ticker}", "description": plt_link, "imagefile": imagefile, }
def chain_command( 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""" # 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.fillna(0) puts_df = options.puts.fillna(0) 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 = [] # Output 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=(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 = save_image(f"opt-chain{i}.png", fig) 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)}") choices = [ disnake.SelectOption(label="Home", value="0", emoji="🟢"), ] return { "view": Menu, "title": title, "embed": embeds, "choices": choices, "embeds_img": embeds_img, }
def vol_command( ticker: str = None, expiry: str = "", min_sp: float = None, max_sp: float = None, ): """Options VOL""" # Debug if cfg.DEBUG: logger.debug("opt-vol %s %s %s %s", ticker, expiry, min_sp, max_sp) # Check for argument if ticker is None: raise Exception("Stock ticker is required") dates = yfinance_model.option_expirations(ticker) if not dates: raise Exception("Stock ticker is invalid") options = yfinance_model.get_option_chain(ticker, expiry) current_price = yfinance_model.get_price(ticker) if min_sp is None: min_strike = 0.75 * current_price else: min_strike = min_sp if max_sp is None: max_strike = 1.25 * current_price else: max_strike = max_sp calls = options.calls puts = options.puts call_v = calls.set_index("strike")["volume"] / 1000 put_v = puts.set_index("strike")["volume"] / 1000 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" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = helpers.uuid_get() fig.write_html(f"in/vol_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/vol_{html_ran}.html)" fig.update_layout( width=800, height=500, ) imagefile = helpers.image_border(imagefile, fig=fig) return { "title": f"Volume for {ticker.upper()} expiring {expiry}", "description": plt_link, "imagefile": imagefile, }
def spos_command(ticker: str = ""): """Net short vs position [Stockgrid]""" # Debug user input if cfg.DEBUG: logger.debug("dps-spos %s", ticker) # Check for argument if ticker == "": raise Exception("Stock ticker is required") ticker = ticker.upper() stock = yf.download(ticker, progress=False) if stock.empty: raise Exception("Stock ticker is invalid") # Retrieve data df = stockgrid_model.get_net_short_position(ticker) # Debug user output if cfg.DEBUG: logger.debug(df.to_string()) # Output data title = f"Stocks: [Stockgrid] Net Short vs Position {ticker}" fig = make_subplots(shared_xaxes=True, specs=[[{"secondary_y": True}]]) fig.add_trace( go.Scatter( name="Position ($)", x=df["dates"].values, y=df["dollar_dp_position"] * 1_000, line=dict(color="#fdc708", width=2), opacity=1, showlegend=False, ), secondary_y=True, ) fig.add_trace( go.Bar( name="Net Short Vol. ($)", x=df["dates"], y=df["dollar_net_volume"], opacity=1, showlegend=False, ), secondary_y=False, ) # Set y-axes titles fig.update_xaxes(dtick="M1", tickformat="%b %d\n%Y") fig.update_yaxes(title_text="<b>Position</b> ($)", secondary_y=True) fig.update_traces(hovertemplate="%{y:.2s}") fig.update_layout( margin=dict(l=0, r=10, t=40, b=20), template=cfg.PLT_TA_STYLE_TEMPLATE, colorway=cfg.PLT_TA_COLORWAY, title=f"Net Short Vol. vs Position for {ticker}", title_x=0.5, yaxis_title="<b>Net Short Vol.</b> ($)", yaxis=dict( side="left", showgrid=False, fixedrange=False, layer="above traces", titlefont=dict(color="#d81aea"), tickfont=dict(color="#d81aea"), nticks=10, ), 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="right", position=0.15, fixedrange=False, titlefont=dict(color="#fdc708"), tickfont=dict(color="#fdc708"), nticks=10, ), hovermode="x unified", ) config = dict({"scrollZoom": True}) imagefile = "dps_spos.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = helpers.uuid_get() fig.write_html(f"in/spos_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/spos_{html_ran}.html)" fig.update_layout( width=800, height=500, ) imagefile = helpers.image_border(imagefile, fig=fig) return { "title": title, "description": plt_link, "imagefile": imagefile, }
def fib_command(ticker="", start="", end=""): """Displays chart with fibonacci retracement [Yahoo Finance]""" # Debug if cfg.DEBUG: logger.debug( "ta-fib %s %s %s", ticker, start, end, ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) f_start = None else: start = datetime.strptime(start, cfg.DATE_FORMAT) f_start = start if end == "": end = datetime.now() f_end = None else: end = datetime.strptime(end, cfg.DATE_FORMAT) f_end = None ticker = ticker.upper() df_stock = bots.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)] start = start.strftime("%Y-%m-%d") end = end.strftime("%Y-%m-%d") ( df_fib, min_date, max_date, min_pr, max_pr, ) = custom_indicators_model.calculate_fib_levels(df_stock, 120, f_start, f_end) levels = df_fib.Price fig, ax = plt.subplots(figsize=(plot_autoscale()), dpi=cfp.PLOT_DPI) ax.plot(df_stock["Adj Close"], "b") ax.plot([min_date, max_date], [min_pr, max_pr], c="k") for i in levels: ax.axhline(y=i, c="g", alpha=0.5) for i in range(5): ax.fill_between(df_stock.index, levels[i], levels[i + 1], alpha=0.6) ax.set_ylabel("Price") ax.set_title(f"Fibonacci Support for {ticker.upper()}") ax.set_xlim(df_stock.index[0], df_stock.index[-1]) ax1 = ax.twinx() ax1.set_ylim(ax.get_ylim()) ax1.set_yticks(levels) ax1.set_yticklabels([0, 0.235, 0.382, 0.5, 0.618, 1]) plt.gcf().autofmt_xdate() fig.tight_layout(pad=1) imagefile = "ta_fib.png" plt.savefig(imagefile) imagefile = image_border(imagefile) return { "title": f"Stocks: Fibonacci-Retracement-Levels {ticker}", "imagefile": imagefile, }
def ftd_command(ticker: str = "", start="", end=""): """Fails-to-deliver data [SEC]""" # Debug user input if cfg.DEBUG: logger.debug("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 = 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 = helpers.uuid_get() 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, ) imagefile = helpers.image_border(imagefile, fig=fig) return { "title": f"Stocks: [SEC] Failure-to-deliver {ticker}", "description": plt_link, "imagefile": imagefile, }
def lastcontracts_command(past_transactions_days: int = 2, num: int = 20): """Displays last government contracts [quiverquant.com]""" # Debug user input if cfg.DEBUG: logger.debug("gov lastcontracts %s %s", past_transactions_days, num) df_contracts = quiverquant_model.get_government_trading("contracts") if df_contracts.empty: logger.debug("No government contracts found") raise Exception("No government contracts found") df_contracts.sort_values("Date", ascending=False) df_contracts["Date"] = pd.to_datetime(df_contracts["Date"]) df_contracts["Date"] = df_contracts["Date"].dt.date df_contracts.drop_duplicates(inplace=True) df_contracts = df_contracts[df_contracts["Date"].isin( df_contracts["Date"].unique()[:past_transactions_days])] df_contracts = df_contracts[["Date", "Ticker", "Amount", "Agency"]][:num] choices = [ disnake.SelectOption(label="Overview", value="0", emoji="🟢"), ] title = "Stocks: [quiverquant.com] Top buy government trading" initial_str = "Overview" i = 1 for col_name in df_contracts["Ticker"].values: menu = f"\nPage {i}: {col_name}" initial_str += f"\nPage {i}: {col_name}" choices.append( disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"), ) i += 1 embeds = [] df_contracts = df_contracts.T reports = [f"{initial_str}"] embeds.append( disnake.Embed( title=title, description=initial_str, colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) for column in df_contracts.columns.values: description = "```" + df_contracts[column].fillna( "").to_string() + "```" embeds.append( disnake.Embed( description=description, colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) reports.append(f"{description}") return { "view": Menu, "title": title, "description": reports, "embed": embeds, "choices": choices, }
def shorted_command(num: int = 10): """Show most shorted stocks [Yahoo Finance]""" # Debug user input if cfg.DEBUG: logger.debug("dps-shorted %s", num) # Check for argument if num < 0: raise Exception("Number has to be above 0") # Retrieve data df = yahoofinance_model.get_most_shorted().head(num) # Debug user output if cfg.DEBUG: logger.debug(df.to_string()) # Output data title = "Stocks: [Yahoo Finance] Most Shorted" df.dropna(how="all", axis=1, inplace=True) df = df.replace(float("NaN"), "") future_column_name = df["Symbol"] df = df.transpose() df.columns = future_column_name df.drop("Symbol") embeds = [] choices = [ disnake.SelectOption(label="Overview", value="0", emoji="🟢"), ] initial_str = "Overview" i = 1 for col_name in df.columns.values: menu = f"\nPage {i}: {col_name}" initial_str += f"\nPage {i}: {col_name}" if i < 19: choices.append( disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"), ) if i == 20: choices.append( disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"), ) i += 1 reports = [f"{initial_str}"] embeds.append( disnake.Embed( title=title, description=initial_str, colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) ) for column in df.columns.values: description = "```" + df[column].fillna("").to_string() + "```" embeds.append( disnake.Embed(description=description, colour=cfg.COLOR,).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) ) reports.append(f"{description}") return { "view": Menu, "title": title, "description": reports, "embed": embeds, "choices": choices, }
def obv_command(ticker="", start="", end=""): """Displays chart with on balance volume [Yahoo Finance]""" # Debug if cfg.DEBUG: logger.debug( "ta-obv %s %s %s", ticker, 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) ticker = ticker.upper() df_stock = 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.obv(df_stock) df_cal = df_ta.values df_cal = df_cal / divisor fig = make_subplots( rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.06, row_width=[0.2, 0.2, 0.2], ) fig.add_trace( go.Scatter( name=ticker, x=df_stock.index, y=df_stock["Adj Close"].values, line=dict(color="#fdc708", width=2), opacity=1, ), row=1, col=1, ) colors = [ "green" if row.Open < row["Adj Close"] else "red" for _, row in df_stock.iterrows() ] fig.add_trace( go.Bar( x=df_stock.index, y=df_vol, name="Volume [M]", marker_color=colors, ), row=2, col=1, ) fig.add_trace( go.Scatter( name="OBC [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=40, b=20), template=cfg.PLT_TA_STYLE_TEMPLATE, colorway=cfg.PLT_TA_COLORWAY, title=f"{ticker} OBV", title_x=0.4, yaxis_title="Stock Price ($)", yaxis2_title="Volume [M]", yaxis3_title="OBV [M]", yaxis=dict(fixedrange=False, ), xaxis=dict( rangeslider=dict(visible=False), type="date", ), dragmode="pan", legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1), ) config = dict({"scrollZoom": True}) imagefile = "ta_obv.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = helpers.uuid_get() fig.write_html(f"in/obv_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/obv_{html_ran}.html)" fig.update_layout( width=800, height=500, ) imagefile = helpers.image_border(imagefile, fig=fig) return { "title": f"Stocks: On-Balance-Volume {ticker}", "description": plt_link, "imagefile": imagefile, }
def pos_command(sort="dpp_dollar", num: int = 10): """Dark pool short position [Stockgrid]""" # Debug user input if cfg.DEBUG: logger.debug("dps-pos %s %s", sort, num) # Check for argument possible_sorts = ("sv", "sv_pct", "nsv", "nsv_dollar", "dpp", "dpp_dollar") if sort not in possible_sorts: raise Exception(f"The possible sorts are: {', '.join(possible_sorts)}") if num < 0: raise Exception("Number has to be above 0") # Retrieve data df = stockgrid_model.get_dark_pool_short_positions(sort, False) df = df.iloc[:num] # Debug user output if cfg.DEBUG: logger.debug(df.to_string()) # Output data title = "Stocks: [Stockgrid] Dark Pool Short Position" dp_date = df["Date"].values[0] df = df.drop(columns=["Date"]) df["Net Short Volume $"] = df["Net Short Volume $"] / 100_000_000 df["Short Volume"] = df["Short Volume"] / 1_000_000 df["Net Short Volume"] = df["Net Short Volume"] / 1_000_000 df["Short Volume %"] = df["Short Volume %"] * 100 df["Dark Pools Position $"] = df["Dark Pools Position $"] / (1_000_000_000) df["Dark Pools Position"] = df["Dark Pools Position"] / 1_000_000 df.columns = [ "Ticker", "Short Vol. (1M)", "Short Vol. %", "Net Short Vol. (1M)", "Net Short Vol. ($100M)", "DP Position (1M)", "DP Position ($1B)", ] future_column_name = df["Ticker"] df = df.transpose() df.columns = future_column_name df.drop("Ticker") embeds = [] choices = [ disnake.SelectOption(label="Overview", value="0", emoji="🟢"), ] initial_str = "Overview" i = 1 for col_name in df.columns.values: menu = f"\nPage {i}: {col_name}" initial_str += f"\nPage {i}: {col_name}" if i < 19: choices.append( disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"), ) if i == 20: choices.append( disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"), ) i += 1 reports = [f"{initial_str}"] embeds.append( disnake.Embed( title=title, description=initial_str, colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) for column in df.columns.values: description = ("```The following data corresponds to the date: " + dp_date + "\n\n" + df[column].fillna("").to_string() + "```", ) embeds.append( disnake.Embed( title=title, description=description, colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) reports.append(f"{description}") return { "view": Menu, "title": title, "description": reports, "embed": embeds, "choices": choices, }
def overview_command(preset: str = "template", sort: str = "", limit: int = 5, ascend: bool = False): """Displays stocks with overview data such as Sector and Industry [Finviz]""" # Check for argument if preset == "template" or preset not in so.all_presets: raise Exception("Invalid preset selected!") # Debug if cfg.DEBUG: logger.debug("scr-overview %s %s %s %s", preset, sort, limit, ascend) # Check for argument if limit < 0: raise Exception("Number has to be above 0") # Output Data df_screen = get_screener_data( preset, "overview", limit, ascend, ) description = "" title = "Stocks: [Finviz] Overview Screener" if isinstance(df_screen, pd.DataFrame): if df_screen.empty: raise Exception("No data found.") df_screen = df_screen.dropna(axis="columns", how="all") if sort: if " ".join(sort) in so.d_cols_to_sort["overview"]: df_screen = df_screen.sort_values( by=[" ".join(sort)], ascending=ascend, na_position="last", ) else: similar_cmd = difflib.get_close_matches( " ".join(sort), so.d_cols_to_sort["overview"], n=1, cutoff=0.7, ) if similar_cmd: description = f"Replacing '{' '.join(sort)}' by '{similar_cmd[0]}' so table can be sorted.\n\n" df_screen = df_screen.sort_values( by=[similar_cmd[0]], ascending=ascend, na_position="last", ) else: raise ValueError( f"Wrong sort column provided! Select from: {', '.join(so.d_cols_to_sort['overview'])}" ) df_screen = df_screen.fillna("") future_column_name = df_screen["Ticker"] df_screen = df_screen.head(n=limit).transpose() df_screen.columns = future_column_name df_screen.drop("Ticker") embeds = [] choices = [ disnake.SelectOption(label="Overview", value="0", emoji="🟢"), ] initial_str = description + "Overview" i = 1 for column in df_screen.columns.values: menu = f"\nPage {i}: {column}" initial_str += f"\nPage {i}: {column}" if i < 19: choices.append( disnake.SelectOption(label=menu, value=f"{i}", emoji="🟢"), ) if i == 20: choices.append( disnake.SelectOption(label="Max Reached", value=f"{i}", emoji="🟢"), ) i += 1 reports = [f"{initial_str}"] embeds.append( disnake.Embed( title=title, description=initial_str, colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) for column in df_screen.columns.values: description = f"```{df_screen[column].fillna('')}```" embeds.append( disnake.Embed( title=title, description=description, colour=cfg.COLOR, ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, )) reports.append(f"{description}") return { "view": Menu, "title": title, "description": reports, "embed": embeds, "choices": choices, }
def dpotc_command(ticker: str = ""): """Dark pools (ATS) vs OTC data [FINRA]""" # Debug user input if cfg.DEBUG: logger.debug("dps-dpotc %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 ats, otc = finra_model.getTickerFINRAdata(ticker) # Debug user output if cfg.DEBUG: logger.debug(ats.to_string()) logger.debug(otc.to_string()) # Output data title = f"Stocks: [FINRA] Dark Pools (ATS) vs OTC {ticker}" if ats.empty and otc.empty: raise Exception("Stock ticker is invalid") fig = make_subplots( rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.07, row_width=[0.4, 0.6], specs=[[{ "secondary_y": True }], [{ "secondary_y": False }]], ) if not ats.empty and not otc.empty: fig.add_trace( go.Bar( x=ats.index, y=(ats["totalWeeklyShareQuantity"] + otc["totalWeeklyShareQuantity"]), name="ATS", opacity=0.8, ), row=1, col=1, secondary_y=False, ) fig.add_trace( go.Bar( x=otc.index, y=otc["totalWeeklyShareQuantity"], name="OTC", opacity=0.8, yaxis="y2", offset=0.0001, ), row=1, col=1, ) elif not ats.empty: fig.add_trace( go.Bar( x=ats.index, y=(ats["totalWeeklyShareQuantity"] + otc["totalWeeklyShareQuantity"]), name="ATS", opacity=0.8, ), row=1, col=1, secondary_y=False, ) elif not otc.empty: fig.add_trace( go.Bar( x=otc.index, y=otc["totalWeeklyShareQuantity"], name="OTC", opacity=0.8, yaxis="y2", secondary_y=False, ), row=1, col=1, ) if not ats.empty: fig.add_trace( go.Scatter( name="ATS", x=ats.index, y=ats["totalWeeklyShareQuantity"] / ats["totalWeeklyTradeCount"], line=dict(color="#fdc708", width=2), opacity=1, showlegend=False, yaxis="y2", ), row=2, col=1, ) if not otc.empty: fig.add_trace( go.Scatter( name="OTC", x=otc.index, y=otc["totalWeeklyShareQuantity"] / otc["totalWeeklyTradeCount"], line=dict(color="#d81aea", width=2), opacity=1, showlegend=False, ), row=2, col=1, ) else: fig.add_trace( go.Scatter( name="OTC", x=otc.index, y=otc["totalWeeklyShareQuantity"] / otc["totalWeeklyTradeCount"], line=dict(color="#d81aea", width=2), opacity=1, showlegend=False, ), row=2, col=1, ) fig.update_xaxes(showspikes=True, spikesnap="cursor", spikemode="across") fig.update_yaxes(showspikes=True, spikethickness=2) fig.update_layout( margin=dict(l=20, r=0, t=10, b=20), template=cfg.PLT_CANDLE_STYLE_TEMPLATE, colorway=cfg.PLT_TA_COLORWAY, title=f"Dark Pools (ATS) vs OTC (Non-ATS) Data for {ticker}", title_x=0.5, yaxis3_title="Shares per Trade", yaxis_title="Total Weekly Shares", xaxis2_title="Weeks", yaxis=dict( fixedrange=False, side="left", nticks=20, ), yaxis2=dict( fixedrange=False, showgrid=False, overlaying="y", anchor="x", layer="above traces", ), yaxis3=dict( fixedrange=False, nticks=10, ), xaxis=dict( rangeslider=dict(visible=False), type="date", showspikes=True, nticks=20, ), legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1), barmode="group", bargap=0.5, bargroupgap=0, dragmode="pan", hovermode="x unified", spikedistance=1000, hoverdistance=100, ) config = dict({"scrollZoom": True}) imagefile = "dps_dpotc.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = helpers.uuid_get() fig.write_html(f"in/dpotc_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/dpotc_{html_ran}.html)" fig.update_layout( width=800, height=500, ) imagefile = helpers.image_border(imagefile, fig=fig) return { "title": title, "description": plt_link, "imagefile": imagefile, }