import asyncio import io from datetime import datetime, timedelta import pandas as pd import requests import streamlit as st from openbb_terminal.core.plots.plotly_helper import OpenBBFigure, theme from openbb_terminal.core.session.current_system import set_system_variable from openbb_terminal.dashboards.stream import streamlit_helpers as st_helpers pd.options.plotting.backend = "plotly" # Suppressing sdk logs set_system_variable("LOGGING_SUPPRESS", True) st.set_page_config( layout="wide", page_title="Short Data", initial_sidebar_state="expanded", ) st.sidebar.markdown( "

Finra Short Data

", unsafe_allow_html=True, ) TICKER = st.sidebar.container() DAYS = st.sidebar.container() COUNT = st.sidebar.container() load_button, ticker_button, show_button = st.sidebar.columns([1, 1, 1]) output1 = st.empty() output2 = st.empty() title_html = """

Load Data:
This widget downloads the consolidated NMS short data from FINRA and aggregates the data by summing over the entire time period.

Note that clicking the this button will reload all data. This can get time consuming, so if you pick a few hundred days, expect a few minutes for loading time.

""" middle_html = """ Plot Ticker:
Query for a single stock. This will work with the loaded data. Note that if you want to reload the data, this will once again take some time. """ st.markdown(title_html, unsafe_allow_html=True) st.markdown(middle_html, unsafe_allow_html=True) MAIN_LOOP: asyncio.AbstractEventLoop = None # type: ignore st_helpers.set_current_page("Short Data") st_helpers.set_css() class FinraShortData: def __init__(self, days_slider=30, count_slider=10): self.df = st_helpers.load_state("df", pd.DataFrame()) self.days_slider = st_helpers.load_state("days_slider", days_slider) self.count_slider = st_helpers.load_state("count_slider", count_slider) self.ticker_button = None self.load_button = None self.show_button = None self.loaded = False def activate_buttons(self): if not self.loaded: with ticker_button.container(): self.ticker_button = ticker_button.button( "Plot Ticker", key="ticker_button" ) with show_button.container(): self.show_button = show_button.button("Show", key="show_button") self.loaded = True def show_button_click(self): output1.empty() self.update() def load_button_click(self): output1.empty() output2.empty() with st.spinner(f"Loading data for {self.days_slider} days"): self.fetch_new_data() self.update() def ticker_button_click(self): output2.empty() self.ticker_plot() def fetch_new_data(self): self.df = pd.DataFrame() today = datetime.now().date() start_date = today - timedelta(days=self.days_slider) dates = pd.date_range(start_date, today) for date in dates: r = requests.get( f"https://cdn.finra.org/equity/regsho/daily/CNMSshvol{date.strftime('%Y%m%d')}.txt", timeout=30, ) if r.status_code == 200: self.df = pd.concat( [self.df, pd.read_csv(io.StringIO(r.text), sep="|")], axis=0 ) self.df = self.df[self.df.Date > 20100101] self.df.Date = self.df["Date"].apply( lambda x: datetime.strptime(str(x), "%Y%m%d") ) st_helpers.save_state("df", self.df) def update(self): if not self.df.empty: temp = ( self.df.groupby("Symbol")[["ShortVolume", "TotalVolume"]] .agg("sum") .sort_values(by="ShortVolume", ascending=False) .head(self.count_slider)[::-1] ) fig = OpenBBFigure() fig.add_bar( x=temp.TotalVolume, y=temp.index, orientation="h", name="Total Volume", marker_color=theme.up_color, ) fig.add_bar( x=temp.ShortVolume, y=temp.index, orientation="h", name="Short Volume", marker_color=theme.down_color, ) fig.update_layout( title=f"Top {self.count_slider} Short Volume in Last {self.days_slider} Days", margin=dict(l=30), xaxis_title="Volume", yaxis_title="Ticker", barmode="stack", bargap=0.1, hovermode="y unified", legend=dict( orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1, ), ) fig.show(external=True) with output1: st.plotly_chart(fig, use_container_width=True) def ticker_plot(self): stock_data = self.df.copy().loc[ self.df.Symbol == self.stock_input.upper(), ["Date", "ShortVolume", "TotalVolume"], ] fig2 = OpenBBFigure() fig2.add_scatter( x=stock_data.Date, y=stock_data.TotalVolume, name="Total Volume", marker_color=theme.up_color, ) fig2.add_scatter( x=stock_data.Date, y=stock_data.ShortVolume, name="Short Volume", marker_color=theme.down_color, ) fig2.update_layout( title=f"Stock Volume and Short Volume for {self.stock_input.upper()}", margin=dict(l=30), xaxis_title="Date", yaxis_title="Volume", ) fig2.show(external=True) with output2: st.plotly_chart(fig2) def build_app(self): with TICKER: self.stock_input = TICKER.text_input("Ticker", "GME", key="ticker") with DAYS: self.days_slider = DAYS.slider( "Days", 1, 1000, 100, help="Number of days to load" ) with COUNT: self.count_slider = COUNT.slider( "Count", 1, 100, 20, help="Number of stocks to plot" ) with load_button.container(): self.load_button = load_button.button("Load Data", key="load") if not self.df.empty: self.activate_buttons() if self.load_button: self.load_button_click() if self.show_button: self.show_button_click() if self.ticker_button: self.ticker_button_click() if not self.df.empty: self.activate_buttons() if __name__ == "__main__": app = FinraShortData() app.build_app()