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 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(dataBytesIO, "PNG", quality=100) dataBytesIO.seek(0) imagefile = image_border(imagefile, base64=dataBytesIO) im.close() return { "title": f"Stocks: [Finviz] Trendlines & Data {ticker.upper()}", "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 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 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 ) # Output data fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) report, im = cnn_model.get_feargreed_report(indicator, fig) ax.axes.xaxis.set_visible(False) ax.axes.yaxis.set_visible(False) if indicator: ax.imshow(im) imagefile = "feargreed.png" dataBytesIO = io.BytesIO() plt.savefig(dataBytesIO) plt.close("all") dataBytesIO.seek(0) imagefile = image_border(imagefile, base64=dataBytesIO) return { "title": "Economy: [CNN] Fear Geed Index", "imagefile": imagefile, "description": report, }
def donchian_command(ticker="", upper_length="25", lower_length="100", start="", end=""): """Displays chart with donchian channel [Yahoo Finance]""" # Debug if cfg.DEBUG: logger.debug( "ta donchian %s %s %s %s %s", ticker, upper_length, lower_length, start, end, ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) if not upper_length.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") upper_length = float(upper_length) if not lower_length.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") lower_length = float(lower_length) ticker = ticker.upper() df_stock = load(ticker, start) if df_stock.empty: raise Exception("Stock ticker is invalid") # Retrieve Data df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)] df_ta = volatility_model.donchian(df_stock["High"], df_stock["Low"], upper_length, lower_length) df_ta = df_ta.fillna(0.0) # Output Data fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) ax.plot(df_stock.index, df_stock["Adj Close"].values, lw=3) ax.plot(df_ta.index, df_ta.iloc[:, 0].values, lw=1.5, label="upper") ax.plot(df_ta.index, df_ta.iloc[:, 1].values, lw=1.5, ls="--") ax.plot(df_ta.index, df_ta.iloc[:, 2].values, lw=1.5, label="lower") ax.set_title(f"{ticker} donchian") ax.set_xlim(df_stock.index[0], df_stock.index[-1]) ax.set_xlabel("Time") ax.set_ylabel("Price ($)") ax.legend([ticker, df_ta.columns[0], df_ta.columns[1], df_ta.columns[2]]) ax.fill_between( df_ta.index, df_ta.iloc[:, 0].values, df_ta.iloc[:, 2].values, alpha=0.1, ) ax.grid(b=True, which="major", color="#666666", linestyle="-") plt.gcf().autofmt_xdate() fig.tight_layout(pad=1) plt.legend() imagefile = "ta_donchian.png" dataBytesIO = io.BytesIO() plt.savefig(dataBytesIO) plt.close("all") dataBytesIO.seek(0) imagefile = image_border(imagefile, base64=dataBytesIO) return { "title": f"Stocks: Donchian-Channels {ticker}", "imagefile": imagefile, }
def cg_command(ticker="", length="14", start="", end=""): """Displays chart with centre of gravity [Yahoo Finance]""" # Debug if cfg.DEBUG: logger.debug( "ta-cg %s %s %s %s", ticker, length, start, end, ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) if not length.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") length = float(length) ticker = ticker.upper() df_stock = 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)] df_close = df_stock["Adj Close"] df_close.columns = ["values"] df_ta = momentum_model.cg(df_close, length) # Output Data fig, axes = plt.subplots(2, 1, figsize=plot_autoscale(), dpi=PLOT_DPI) ax = axes[0] ax.set_title(f"{ticker} Centre of Gravity") ax.plot(df_stock.index, df_stock["Adj Close"].values, "k", lw=1) ax.set_xlim(df_stock.index[0], df_stock.index[-1]) ax.set_ylabel("Share Price ($)") ax.grid(b=True, which="major", color="#666666", linestyle="-") ax2 = axes[1] ax2.plot(df_ta.index, df_ta.values, "b", lw=2, label="CG") # shift cg 1 bar forward for signal signal = df_ta.values signal = np.roll(signal, 1) ax2.plot(df_ta.index, signal, "g", lw=1, label="Signal") ax2.set_xlim(df_stock.index[0], df_stock.index[-1]) ax2.grid(b=True, which="major", color="#666666", linestyle="-") plt.gcf().autofmt_xdate() fig.tight_layout(pad=1) plt.legend() imagefile = "ta_cg.png" plt.savefig(imagefile) imagefile = image_border(imagefile) return { "title": f"Stocks: Center-of-Gravity {ticker}", "imagefile": imagefile, }
def historical_command(signal: str = "", start=""): """Displays historical price comparison between similar companies [Yahoo Finance]""" # Debug user input if cfg.DEBUG: logger.debug("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.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." ) return { "title": "ERROR Stocks: [Yahoo Finance] Historical Screener", "description": error, } 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]]) imagefile = "scr_historical.png" plt.savefig(imagefile) imagefile = image_border(imagefile) return { "title": "Stocks: [Yahoo Finance] Historical Screener", "description": description, "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 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 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 gtrades_command( ticker: str = "", gov_type="", past_transactions_months: int = 10, raw: bool = False, ): """Displays government trades [quiverquant.com]""" # Debug user input if cfg.DEBUG: logger.debug( "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) raise Exception(f"No recent {gov_type} trading data found") df_gov["min"] = df_gov["Range"].apply( lambda x: x.split("-")[0].strip("$").replace(",", "").strip() ) df_gov["max"] = df_gov["Range"].apply( lambda x: x.split("-")[1].replace(",", "").strip().strip("$") if "-" in x else x.strip("$").replace(",", "").split("\n")[0] ) df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply( lambda x: int(float(x["min"])) if x["Transaction"] == "Purchase" else -int(float(x["max"])), axis=1, ) df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply( lambda x: int(float(x["max"])) if x["Transaction"] == "Purchase" else -1 * int(float(x["min"])), axis=1, ) df_gov = df_gov.sort_values("TransactionDate", ascending=True) fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) ax.fill_between( df_gov["TransactionDate"].unique(), df_gov.groupby("TransactionDate")["lower"].sum().values / 1000, df_gov.groupby("TransactionDate")["upper"].sum().values / 1000, ) ax.set_xlim( [ df_gov["TransactionDate"].values[0], df_gov["TransactionDate"].values[-1], ] ) ax.grid() ax.set_title(f"{gov_type.capitalize()} trading on {ticker}") ax.set_xlabel("Date") ax.set_ylabel("Amount ($1k)") plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%Y/%m/%d")) plt.gcf().autofmt_xdate() fig.tight_layout() plt.savefig("gov_gtrades.png") imagefile = "gov_gtrades.png" imagefile = image_border(imagefile) return { "title": "Stocks: [quiverquant.com] Government Trades", "imagefile": imagefile, }
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, }
def candle_command( 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 """ 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" or "^" crypto = "-USD" 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( shared_xaxes=True, vertical_spacing=0.09, subplot_titles=plt_title, specs=[[{ "secondary_y": True }]], ) 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, ), secondary_y=True, ) fig.add_trace( go.Bar( x=df_stock.index, y=df_stock.Volume, name="Volume", yaxis="y2", marker_color="#d81aea", opacity=0.4, showlegend=False, ), secondary_y=False, ) if interval != 1440: fig.add_trace( go.Scatter( name="VWAP", x=df_stock.index, y=df_vwap["VWAP_D"], opacity=0.65, line=dict(color="#fdc708", width=2), showlegend=True, ), secondary_y=True, ) fig.update_xaxes(showspikes=True, spikesnap="cursor", spikemode="across") fig.update_yaxes(showspikes=True, spikethickness=2) fig.update_layout( margin=dict(l=0, r=0, t=40, b=20), template=cfg.PLT_CANDLE_STYLE_TEMPLATE, yaxis2_title="Price ($)", yaxis_title="Volume", yaxis=dict( showgrid=False, fixedrange=False, side="left", titlefont=dict(color="#d81aea"), tickfont=dict(color="#d81aea"), nticks=20, ), yaxis2=dict( side="right", fixedrange=False, anchor="x", layer="above traces", overlaying="y", nticks=20, ), 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", spikedistance=1000, hoverdistance=100, ) if futures in ticker.upper(): fig.update_xaxes(rangebreaks=[ dict(bounds=["sat", "sun"]), dict(bounds=[17, 17.50], pattern="hour"), ], ) elif crypto not in ticker.upper() and interval != 1440: fig.update_xaxes(rangebreaks=[ dict(bounds=["sat", "mon"]), dict(bounds=[20, 9.50], pattern="hour"), ], ) config = dict({"scrollZoom": True}) imagefile = "candle.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = helpers.uuid_get() fig.write_html(f"in/candle_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/candle_{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 psi_command(ticker: str = ""): """Price vs short interest volume [Stockgrid]""" # Debug user input if cfg.DEBUG: logger.debug("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 fig = make_subplots( rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.06, row_width=[0.3, 0.6], specs=[[{ "secondary_y": True }], [{ "secondary_y": False }]], ) fig.add_trace( go.Scatter( name=ticker, x=df["date"].values, y=prices[len(prices) - len(df):], line=dict(color="#fdc708", width=2), opacity=1, showlegend=False, ), row=1, col=1, secondary_y=False, ) fig.add_trace( go.Bar( x=df["date"], y=df["total_volume"] / 1_000_000, name="Total Volume", yaxis="y2", ), row=1, col=1, secondary_y=True, ) fig.add_trace( go.Bar( x=df["date"], y=df["short_volume"] / 1_000_000, name="Short Volume", yaxis="y2", ), row=1, col=1, secondary_y=True, ) fig.add_trace( go.Scatter( name="Short Vol. %", x=df["date"].values, y=100 * df["short_volume%"], line=dict(width=2), opacity=1, showlegend=False, ), row=2, col=1, secondary_y=False, ) fig.update_traces(hovertemplate="%{y:.2f}") fig.update_xaxes(showspikes=True, spikesnap="cursor", spikemode="across") fig.update_yaxes(showspikes=True, spikethickness=2) 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"Price vs Short Volume Interest for {ticker}", title_x=0.45, yaxis_title="Stock Price ($)", yaxis2_title="FINRA Volume [M]", yaxis3_title="Short Vol. %", yaxis=dict( side="right", fixedrange=False, titlefont=dict(color="#fdc708"), tickfont=dict(color="#fdc708"), nticks=20, ), yaxis2=dict( side="left", fixedrange=False, anchor="x", overlaying="y", titlefont=dict(color="#d81aea"), tickfont=dict(color="#d81aea"), nticks=20, ), yaxis3=dict( fixedrange=False, titlefont=dict(color="#9467bd"), tickfont=dict(color="#9467bd"), nticks=20, ), xaxis=dict( rangeslider=dict(visible=False), type="date", ), dragmode="pan", legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1), hovermode="x unified", spikedistance=1000, hoverdistance=100, ) config = dict({"scrollZoom": True}) imagefile = "dps_psi.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = helpers.uuid_get() fig.write_html(f"in/psi_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/psi_{html_ran}.html)" fig.update_layout( width=800, height=500, ) imagefile = helpers.image_border(imagefile, fig=fig) return { "title": f"Stocks: [Stockgrid] Price vs Short Interest Volume {ticker}", "description": plt_link, "imagefile": imagefile, }
def qtrcontracts_command(num: int = 20, analysis=""): """Displays a look at government contracts [quiverquant.com]""" # Debug user input if cfg.DEBUG: logger.debug("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) # 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" imagefile = image_border(imagefile) output = { "title": "Stocks: [quiverquant.com] Government Contracts", "imagefile": imagefile, "description": description, } 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]"] output = { "title": "Stocks: [quiverquant.com] Government Contracts", "description": tickers.to_string(), } return output
def vsurf_command( ticker: str = "", z: str = "IV", ): """Display vol surface Parameters ---------- ticker: Stock Ticker z : The variable for the Z axis """ # Debug if cfg.DEBUG: logger.debug("opt-oi %s %s", 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), 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" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = helpers.uuid_get() fig.write_html(f"in/vsurf_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/vsurf_{html_ran}.html)" fig.update_layout( width=800, height=500, ) imagefile = helpers.image_border(imagefile, fig=fig) return { "title": f"{label} Surface for {ticker.upper()}", "description": plt_link, "imagefile": imagefile, }
def cc_hist_command(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 """ # 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", ] 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 = np.random.randint(70000) imagefile = f"opt_hist{rand}.png" fig.write_image(imagefile) imagefile = image_border(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)" return { "title": f"{ticker.upper()} {strike} {opt_type} expiring {expiry} Historical", "description": plt_link, "imagefile": imagefile, }
def macd_command(ticker="", fast="12", slow="26", signal="9", start="", end=""): """Displays chart with moving average convergence/divergence [Yahoo Finance]""" # Debug if cfg.DEBUG: # pylint: disable=logging-too-many-args logger.debug( "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 = 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 = helpers.uuid_get() 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, ) imagefile = helpers.image_border(imagefile, fig=fig) return { "title": f"Stocks: Moving-Average-Convergence-Divergence {ticker}", "description": plt_link, "imagefile": imagefile, }
def rsi_command(ticker="", length="14", scalar="100", drift="1", start="", end=""): """Displays chart with relative strength index [Yahoo Finance]""" # Debug if cfg.DEBUG: # pylint: disable=logging-too-many-args logger.debug( "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 = 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 = helpers.uuid_get() 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, ) imagefile = helpers.image_border(imagefile, fig=fig) return { "title": f"Stocks: Relative-Strength-Index {ticker}", "description": plt_link, "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 cci_command(ticker="", length="14", scalar="0.015", start="", end=""): """Displays chart with commodity channel index [Yahoo Finance]""" # Debug if cfg.DEBUG: logger.debug( "ta-cci %s %s %s %s %s", ticker, length, scalar, start, end, ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) # pylint try: length = int(length) except ValueError as e: raise Exception("Length has to be an integer") from e try: scalar = float(scalar) except ValueError as e: raise Exception("Scalar has to be an integer") from e 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)] df_ta = momentum_model.cci( df_stock["High"], df_stock["Low"], df_stock["Adj Close"], length, scalar ) dmin = df_ta.values.min() dmax = df_ta.values.max() # Output Data fig = make_subplots( rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.07, row_width=[0.5, 0.6], ) fig.add_trace( go.Scatter( name=ticker, x=df_stock.index, y=df_stock["Adj Close"].values, line=dict(color="#fdc708", width=2), opacity=1, showlegend=False, ), row=1, col=1, ) fig.add_trace( go.Scatter( showlegend=False, x=df_ta.index, y=df_ta.iloc[:, 0].values, opacity=1, ), row=2, col=1, ) fig.add_hrect( y0=100, y1=dmax, fillcolor="red", opacity=0.2, layer="below", line_width=0, row=2, col=1, ) fig.add_hrect( y0=-100, y1=dmin, fillcolor="green", opacity=0.2, layer="below", line_width=0, row=2, col=1, ) fig.add_hline( y=-100, fillcolor="green", opacity=1, layer="below", line_width=3, line=dict(color="green", dash="dash"), row=2, col=1, ) fig.add_hline( y=100, fillcolor="red", opacity=1, layer="below", line_width=3, line=dict(color="red", dash="dash"), row=2, col=1, ) fig.update_layout( margin=dict(l=0, r=20, t=30, b=20), template=cfg.PLT_TA_STYLE_TEMPLATE, colorway=cfg.PLT_TA_COLORWAY, title=f"{ticker} CCI", 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_cci.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = helpers.uuid_get() 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, ) imagefile = helpers.image_border(imagefile, fig=fig) return { "title": f"Stocks: Commodity-Channel-Index {ticker}", "description": plt_link, "imagefile": imagefile, }
def topsells_command( gov_type="", past_transactions_months: int = 5, num: int = 10, raw: bool = False, ): """Displays most sold stocks by the congress/senate/house [quiverquant.com]""" # Debug user input if cfg.DEBUG: logger.debug( "gov-topsells %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) # Output Data if df_gov.empty: raise Exception(f"No {gov_type} trading data found\n") df_gov = df_gov.sort_values("TransactionDate", ascending=False) start_date = datetime.now() - timedelta(days=past_transactions_months * 30) df_gov["TransactionDate"] = pd.to_datetime(df_gov["TransactionDate"]) df_gov = df_gov[df_gov["TransactionDate"] > start_date].dropna() df_gov["Range"] = df_gov["Range"].apply(lambda x: "$5,000,001-$5,000,001" if x == ">$5,000,000" else x) df_gov["min"] = df_gov["Range"].apply(lambda x: x.split("-")[0].strip( "$").replace(",", "").strip().replace(">$", "").strip()) df_gov["max"] = df_gov["Range"].apply( lambda x: x.split("-")[1].replace(",", "").strip().strip("$").replace( ">$", "").strip() if "-" in x else x.strip("$").replace( ",", "").replace(">$", "").strip()) df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply( lambda x: float(x["min"]) if x["Transaction"] == "Purchase" else -float(x["max"]), axis=1, ) df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply( lambda x: float(x["max"]) if x["Transaction"] == "Purchase" else -float(x["min"]), axis=1, ) description = None df_gov = df_gov.sort_values("TransactionDate", ascending=True) if raw: df = pd.DataFrame( df_gov.groupby("Ticker")["upper"].sum().div(1000).sort_values( ascending=True).abs().head(n=num)) description = "```" + df.to_string() + "```" fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) df_gov.groupby("Ticker")["upper"].sum().div(1000).sort_values().abs().head( n=num).plot(kind="bar", rot=0, ax=ax) ax.set_ylabel("Amount ($1k)") ax.set_title( f"{num} most sold stocks over last {past_transactions_months} months" f" (upper bound) for {gov_type}") plt.gcf().axes[0].yaxis.get_major_formatter().set_scientific(False) fig.tight_layout() plt.savefig("gov_topsells.png") imagefile = "gov_topsells.png" imagefile = image_border(imagefile) return { "title": f"Stocks: [quiverquant.com] Top sells for {gov_type.upper()}", "imagefile": imagefile, "description": description, }
def aroon_command(ticker="", length="25", scalar="100", start="", end=""): """Displays chart with aroon indicator [Yahoo Finance]""" # Debug if cfg.DEBUG: logger.debug( "ta-aroon %s %s %s %s %s", ticker, length, scalar, start, end, ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) if not length.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") length = int(length) if not scalar.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") scalar = float(scalar) ticker = ticker.upper() df_stock = helpers.load(ticker, start) if df_stock.empty: raise Exception("Stock ticker is invalid") # Retrieve Data df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)] df_ta = trend_indicators_model.aroon( df_stock["High"], df_stock["Low"], length, scalar ) # Output Data aadown = df_ta.columns[0].replace("_", " ") aaup = df_ta.columns[1].replace("_", " ") aaosc = df_ta.columns[2].replace("_", " ") fig = make_subplots( rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.07, 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, showlegend=False, ), row=1, col=1, ) fig.add_trace( go.Scatter( name=f"Aroon DOWN ({aadown})", x=df_ta.index, y=df_ta.iloc[:, 0].values, opacity=1, ), row=2, col=1, ) fig.add_trace( go.Scatter( name=f"Aroon UP ({aaup})", x=df_ta.index, y=df_ta.iloc[:, 1].values, opacity=1, ), row=2, col=1, ) fig.add_trace( go.Scatter( name=f"Aroon OSC ({aaosc})", x=df_ta.index, y=df_ta.iloc[:, 2].values, opacity=1, ), row=3, col=1, ) fig.add_hline( y=50, fillcolor="grey", opacity=1, layer="below", line_width=3, line=dict(color="grey", 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"Aroon on {ticker}", 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_aroon.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = helpers.uuid_get() fig.write_html(f"in/aroon_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/aroon_{html_ran}.html)" fig.update_layout( width=800, height=500, ) imagefile = helpers.image_border(imagefile, fig=fig) return { "title": f"Stocks: Aroon-Indicator {ticker}", "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 bbands_command(ticker="", length="5", n_std="2", mamode="sma", start="", end=""): """Displays chart with bollinger bands [Yahoo Finance]""" # Debug if cfg.DEBUG: # pylint: disable=logging-too-many-args logger.debug( "ta-bbands %s %s %s %s %s %s", ticker, length, n_std, mamode, start, end, ) # Check for argument possible_ma = [ "dema", "ema", "fwma", "hma", "linreg", "midpoint", "pwma", "rma", "sinwma", "sma", "swma", "t3", "tema", "trima", "vidya", "wma", "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 = float(length) if not n_std.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") n_std = float(n_std) if mamode not in possible_ma: raise Exception("Invalid ma entered") 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)] df_ta = volatility_model.bbands(df_stock["Adj Close"], length, n_std, mamode) # Output Data bbu = df_ta.columns[2].replace("_", " ") bbm = df_ta.columns[1].replace("_", " ") bbl = df_ta.columns[0].replace("_", " ") fig = go.Figure() fig.add_trace( go.Scatter( name=f"{bbu}", x=df_ta.index, y=df_ta.iloc[:, 2].values, opacity=1, mode="lines", line_color="indigo", ), ) fig.add_trace( go.Scatter( name=f"{bbl}", 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)", ), ) fig.add_trace( go.Scatter( name=f"{bbm}", 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} Bollinger Bands ({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_bbands.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = helpers.uuid_get() fig.write_html(f"in/bbands_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/bbands_{html_ran}.html)" fig.update_layout( width=800, height=500, ) imagefile = helpers.image_border(imagefile, fig=fig) return { "title": f"Stocks: Bollinger-Bands {ticker}", "description": plt_link, "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 stoch_command(ticker="", fast_k="14", slow_d="3", slow_k="3", start="", end=""): """Displays chart with stochastic relative strength average [Yahoo Finance]""" # Debug if cfg.DEBUG: logger.debug( "ta-stoch %s %s %s %s %s %s", ticker, fast_k, slow_k, slow_d, start, end, ) # Check for argument if ticker == "": raise Exception("Stock ticker is required") if start == "": start = datetime.now() - timedelta(days=365) else: start = datetime.strptime(start, cfg.DATE_FORMAT) if end == "": end = datetime.now() else: end = datetime.strptime(end, cfg.DATE_FORMAT) if not fast_k.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") fast_k = int(fast_k) if not slow_k.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") slow_k = int(slow_k) if not slow_d.lstrip("-").isnumeric(): raise Exception("Number has to be an integer") slow_d = int(slow_d) ticker = ticker.upper() df_stock = helpers.load(ticker, start) if df_stock.empty: raise Exception("Stock ticker is invalid") # Retrieve Data df_stock = df_stock.loc[(df_stock.index >= start) & (df_stock.index < end)] df_ta = momentum_model.stoch( df_stock["High"], df_stock["Low"], df_stock["Adj Close"], fast_k, slow_d, slow_k, ) fig = make_subplots( rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.07, row_width=[0.5, 0.7], ) fig.add_trace( go.Scatter( x=df_stock.index, y=df_stock["Adj Close"].values, line=dict(color="#fdc708", width=2), opacity=1, showlegend=False, ), row=1, col=1, ) K = df_ta.columns[0].replace("_", " ") D = df_ta.columns[1].replace("_", " ") fig.add_trace( go.Scatter( name=f"%K {K}", x=df_stock.index, y=df_ta.iloc[:, 0].values, line=dict(width=1.8), opacity=1, ), row=2, col=1, ) fig.add_trace( go.Scatter( name=f"%D {D}", x=df_stock.index, y=df_ta.iloc[:, 1].values, line=dict(width=1.8, dash="dash"), opacity=1, ), row=2, col=1, ) fig.add_hrect( y0=80, y1=100, fillcolor="red", opacity=0.2, layer="below", line_width=0, row=2, col=1, ) fig.add_hrect( y0=0, y1=20, fillcolor="green", opacity=0.2, layer="below", line_width=0, row=2, col=1, ) fig.add_hline( y=80, fillcolor="green", opacity=1, layer="below", line_width=3, line=dict(color="red", dash="dash"), row=2, col=1, ) fig.add_hline( y=20, 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=0, t=40, b=20), template=cfg.PLT_TA_STYLE_TEMPLATE, colorway=cfg.PLT_TA_COLORWAY, title=f"Stochastic Relative Strength Index (STOCH RSI) on {ticker}", 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_stoch.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = helpers.uuid_get() fig.write_html(f"in/stoch_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/stoch_{html_ran}.html)" fig.update_layout( width=800, height=500, ) imagefile = helpers.image_border(imagefile, fig=fig) return { "title": f"Stocks: Stochastic-Relative-Strength-Index {ticker}", "description": plt_link, "imagefile": imagefile, }
def hist_command( 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 """ # 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}) imagefile = "opt_hist.png" # Check if interactive settings are enabled plt_link = "" if cfg.INTERACTIVE: html_ran = helpers.uuid_get() fig.write_html(f"in/hist_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/hist_{html_ran}.html)" fig.update_layout( width=800, height=500, ) imagefile = helpers.image_border(imagefile, fig=fig) return { "title": f"{ticker.upper()} {strike} {opt_type} expiring {expiry} Historical", "description": plt_link, "imagefile": imagefile, }
def overview_command( ticker: str = None, expiry: str = None, min_sp: float = None, max_sp: float = None, ): """Options Overview""" # Debug if cfg.DEBUG: logger.debug("opt-overview %s %s %s %s", 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.fillna(0) puts = options.puts.fillna(0) 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" plt_link = "" if cfg.INTERACTIVE: html_ran = helpers.uuid_get() fig.write_html(f"in/oi_{html_ran}.html", config=config) plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/oi_{html_ran}.html)" fig.update_layout( width=800, height=500, ) imagefile = helpers.image_border(imagefile, fig=fig) uploaded_image_oi = gst_imgur.upload_image(imagefile, title="something") image_link_oi = uploaded_image_oi.link os.remove(imagefile) 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 = "" titles = [f"{ticker.upper()} Overview", f"{pfix}Open Interest{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) figc = 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 = helpers.save_image("opt-calls.png", figc) uploaded_image = gst_imgur.upload_image(imagefile, title="something") image_link = uploaded_image.link embeds_img.append( f"{image_link}", ) titles.append(f"{pfix}Calls{sfix}") 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 = helpers.save_image("opt-puts.png", figp) uploaded_image = gst_imgur.upload_image(imagefile, title="something") image_link = uploaded_image.link embeds_img.append( f"{image_link}", ) titles.append(f"{pfix}Puts{sfix}") 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)}") os.remove(imagefile) return { "view": Menu, "titles": titles, "embed": embeds, "choices": choices, "embeds_img": embeds_img, }