Update app.py
Browse files
app.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
import streamlit as st
|
| 4 |
import pandas as pd
|
| 5 |
import numpy as np
|
| 6 |
-
|
| 7 |
|
| 8 |
import os
|
| 9 |
import time
|
|
@@ -100,19 +100,12 @@ def get_options_merge(current_datetime):
|
|
| 100 |
DF_options, tickerlst = get_options_merge(current_datetime)
|
| 101 |
|
| 102 |
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
# Title
|
| 108 |
-
st.title("📊 Options
|
| 109 |
-
|
| 110 |
-
|
| 111 |
|
| 112 |
st.write(f'Number of avialable tickers: {len(tickerlst)}')
|
| 113 |
|
| 114 |
-
|
| 115 |
-
st.write(f'Number of options records: {len(DF_options)}')
|
| 116 |
|
| 117 |
# Display options data
|
| 118 |
st.header("Options Data")
|
|
@@ -120,56 +113,118 @@ st.header("Options Data")
|
|
| 120 |
# Sidebar
|
| 121 |
st.sidebar.header("Controls")
|
| 122 |
st.sidebar.markdown("### Filter Options Data")
|
| 123 |
-
# Add volume and open interest filters in sidebar
|
| 124 |
-
min_volume = st.sidebar.number_input("Minimum Volume", min_value=0, value=100)
|
| 125 |
-
min_open_interest = st.sidebar.number_input("Minimum Open Interest", min_value=0, value=100)
|
| 126 |
-
min_vol_oi_ratio = st.sidebar.number_input("Minimum Volume/Open Interest Ratio", min_value=0.0, value=0.5, step=0.1)
|
| 127 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
|
| 129 |
st.sidebar.markdown("---") # Add a horizontal line as a visual separator
|
| 130 |
|
| 131 |
st.sidebar.markdown("### Filter Stock Data")
|
| 132 |
min_relative_volume = st.sidebar.number_input("Minimum Relative Volume", min_value=0.0, value=1.5, step=0.1)
|
| 133 |
-
min_put_call_volume = st.sidebar.number_input("Minimum Put/Call Volume Ratio", min_value=0.0, value=0.0, step=0.1)
|
| 134 |
-
min_put_call_oi = st.sidebar.number_input("Minimum Put/Call OI Ratio", min_value=0.0, value=0.0, step=0.1)
|
| 135 |
-
|
| 136 |
-
# Filter the dataframe
|
| 137 |
-
filtered_df = DF_options[
|
| 138 |
-
(DF_options['volume'] >= min_volume) &
|
| 139 |
-
(DF_options['openInterest'] >= min_open_interest) &
|
| 140 |
-
(DF_options['Relative Volume'] >= min_relative_volume) &
|
| 141 |
-
(DF_options['Put_Call_Volume_Ratio'] >= min_put_call_volume) &
|
| 142 |
-
(DF_options['Put_Call_OI_Ratio'] >= min_put_call_oi) &
|
| 143 |
-
(DF_options['Volume_OpenInterest_Ratio'] >= min_vol_oi_ratio)
|
| 144 |
-
]
|
| 145 |
-
|
| 146 |
-
st.write(f"Filtered records: {len(filtered_df)} rows")
|
| 147 |
-
|
| 148 |
|
| 149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
|
| 151 |
-
|
| 152 |
-
"
|
| 153 |
-
|
| 154 |
-
|
|
|
|
|
|
|
| 155 |
)
|
| 156 |
|
| 157 |
-
if
|
| 158 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
|
| 160 |
# Download button for the DataFrame
|
| 161 |
-
csv = convert_df(filtered_df)
|
| 162 |
-
st.download_button(
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
)
|
| 168 |
|
| 169 |
-
st.write(f"Filtered Tickers: {filtered_df['Ticker'].unique()} ")
|
| 170 |
|
| 171 |
|
| 172 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
|
| 174 |
st.sidebar.markdown("---") # Add a horizontal line as a visual separator
|
| 175 |
st.sidebar.header("Advanced")
|
|
|
|
| 3 |
import streamlit as st
|
| 4 |
import pandas as pd
|
| 5 |
import numpy as np
|
| 6 |
+
import plotly.express as px
|
| 7 |
|
| 8 |
import os
|
| 9 |
import time
|
|
|
|
| 100 |
DF_options, tickerlst = get_options_merge(current_datetime)
|
| 101 |
|
| 102 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
# Title
|
| 104 |
+
st.title("📊 Unusual Options Activity Dashboard")
|
|
|
|
|
|
|
| 105 |
|
| 106 |
st.write(f'Number of avialable tickers: {len(tickerlst)}')
|
| 107 |
|
| 108 |
+
st.write(f'Number of options contract records: {len(DF_options)}')
|
|
|
|
| 109 |
|
| 110 |
# Display options data
|
| 111 |
st.header("Options Data")
|
|
|
|
| 113 |
# Sidebar
|
| 114 |
st.sidebar.header("Controls")
|
| 115 |
st.sidebar.markdown("### Filter Options Data")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
|
| 117 |
+
# Change number inputs to range sliders for volume and open interest
|
| 118 |
+
volume_range = st.sidebar.slider(
|
| 119 |
+
"Volume Range",
|
| 120 |
+
min_value=0,
|
| 121 |
+
max_value=DF_options['volume'].max().astype(int),
|
| 122 |
+
value=(0, DF_options['volume'].max().astype(int) ),
|
| 123 |
+
step=100
|
| 124 |
+
)
|
| 125 |
+
|
| 126 |
+
open_interest_range = st.sidebar.slider(
|
| 127 |
+
"Open Interest Range",
|
| 128 |
+
min_value=0,
|
| 129 |
+
max_value=DF_options['openInterest'].max().astype(int),
|
| 130 |
+
value=(0, DF_options['openInterest'].max().astype(int) ),
|
| 131 |
+
step=100
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
# Add range selector for Volume/Open Interest Ratio
|
| 135 |
+
vol_oi_ratio_range = st.sidebar.slider(
|
| 136 |
+
"Volume/Open Interest Ratio Range",
|
| 137 |
+
min_value=0.0,
|
| 138 |
+
max_value=np.nanmax(DF_options['Volume_OpenInterest_Ratio'][~np.isinf(DF_options['Volume_OpenInterest_Ratio'])]),
|
| 139 |
+
value=(0.5, np.nanmax(DF_options['Volume_OpenInterest_Ratio'][~np.isinf(DF_options['Volume_OpenInterest_Ratio'])])/2),
|
| 140 |
+
step=0.1
|
| 141 |
+
)
|
| 142 |
|
| 143 |
st.sidebar.markdown("---") # Add a horizontal line as a visual separator
|
| 144 |
|
| 145 |
st.sidebar.markdown("### Filter Stock Data")
|
| 146 |
min_relative_volume = st.sidebar.number_input("Minimum Relative Volume", min_value=0.0, value=1.5, step=0.1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
|
| 148 |
+
# Changed to range sliders
|
| 149 |
+
put_call_volume_range = st.sidebar.slider(
|
| 150 |
+
"Put/Call Volume Ratio Range",
|
| 151 |
+
min_value=0.0,
|
| 152 |
+
max_value=10.0,
|
| 153 |
+
value=(0.0, 0.6),
|
| 154 |
+
step=0.1
|
| 155 |
+
)
|
| 156 |
|
| 157 |
+
put_call_oi_range = st.sidebar.slider(
|
| 158 |
+
"Put/Call OI Ratio Range",
|
| 159 |
+
min_value=0.0,
|
| 160 |
+
max_value=10.0,
|
| 161 |
+
value=(0.0, 3.0),
|
| 162 |
+
step=0.1
|
| 163 |
)
|
| 164 |
|
| 165 |
+
if st.sidebar.button("Filter"):
|
| 166 |
+
# Filter the dataframe with the range
|
| 167 |
+
filtered_df = DF_options[
|
| 168 |
+
(DF_options['volume'] >= volume_range[0]) &
|
| 169 |
+
(DF_options['volume'] <= volume_range[1]) &
|
| 170 |
+
(DF_options['openInterest'] >= open_interest_range[0]) &
|
| 171 |
+
(DF_options['openInterest'] <= open_interest_range[1]) &
|
| 172 |
+
(DF_options['Relative Volume'] >= min_relative_volume) &
|
| 173 |
+
(DF_options['Put_Call_Volume_Ratio'] >= put_call_volume_range[0]) &
|
| 174 |
+
(DF_options['Put_Call_Volume_Ratio'] <= put_call_volume_range[1]) &
|
| 175 |
+
(DF_options['Put_Call_OI_Ratio'] >= put_call_oi_range[0]) &
|
| 176 |
+
(DF_options['Put_Call_OI_Ratio'] <= put_call_oi_range[1]) &
|
| 177 |
+
(DF_options['Volume_OpenInterest_Ratio'] >= vol_oi_ratio_range[0]) &
|
| 178 |
+
(DF_options['Volume_OpenInterest_Ratio'] <= vol_oi_ratio_range[1])
|
| 179 |
+
]
|
| 180 |
+
|
| 181 |
+
st.write(f"Filtered records: {len(filtered_df)} rows")
|
| 182 |
+
|
| 183 |
+
interestedColumns = ['contractSymbol' , 'volume', 'openInterest', 'impliedVolatility', 'Volume_OpenInterest_Ratio' , 'Relative Volume' , 'Put_Call_Volume_Ratio' , 'Put_Call_OI_Ratio' , ]
|
| 184 |
+
|
| 185 |
+
selected_columns = st.multiselect(
|
| 186 |
+
"Select columns to display",
|
| 187 |
+
options=filtered_df.columns.tolist(),
|
| 188 |
+
default=interestedColumns
|
| 189 |
+
)
|
| 190 |
+
|
| 191 |
+
if selected_columns:
|
| 192 |
+
st.dataframe(filtered_df[selected_columns])
|
| 193 |
|
| 194 |
# Download button for the DataFrame
|
| 195 |
+
csv = convert_df(filtered_df)
|
| 196 |
+
st.download_button(
|
| 197 |
+
label="Download Options Data as CSV",
|
| 198 |
+
data=csv,
|
| 199 |
+
file_name=f'options_data_{current_datetime}.csv',
|
| 200 |
+
mime='text/csv',
|
| 201 |
+
)
|
| 202 |
|
| 203 |
+
st.write(f"Filtered Tickers: {filtered_df['Ticker'].unique()} ")
|
| 204 |
|
| 205 |
|
| 206 |
|
| 207 |
+
st.sidebar.markdown("---") # Add a horizontal line as a visual separator
|
| 208 |
+
st.sidebar.header("Ticker")
|
| 209 |
+
|
| 210 |
+
selectedTicker = st.sidebar.selectbox("Select Ticker", options=tickerlst, index=tickerlst.index('AAPL') if 'AAPL' in tickerlst else 0)
|
| 211 |
+
|
| 212 |
+
if st.sidebar.button("Submit"):
|
| 213 |
+
st.write(f"Ticker {selectedTicker}")
|
| 214 |
+
# filter DF_options for selectedTicker
|
| 215 |
+
filtered_ticker_df = DF_options[DF_options['Ticker'] == selectedTicker]
|
| 216 |
+
#plot plotly3D scatter plot for filtered_ticker_df for volume, openInterest and impliedVolatility
|
| 217 |
+
fig = px.scatter_3d(filtered_ticker_df, x='volume', y='openInterest', z='impliedVolatility', color='Type')
|
| 218 |
+
fig.update_layout(
|
| 219 |
+
autosize=True,
|
| 220 |
+
width=2560,
|
| 221 |
+
height=600,
|
| 222 |
+
margin=dict(l=50, r=50, b=50, t=50)
|
| 223 |
+
)
|
| 224 |
+
st.plotly_chart(fig) #, use_container_width=True)
|
| 225 |
+
# Display the filtered DataFrame
|
| 226 |
+
|
| 227 |
+
|
| 228 |
|
| 229 |
st.sidebar.markdown("---") # Add a horizontal line as a visual separator
|
| 230 |
st.sidebar.header("Advanced")
|