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 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 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 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 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 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 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 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 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 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 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 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 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 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, }