AmirTrader commited on
Commit
b835338
·
verified ·
1 Parent(s): 8667e7c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +137 -92
app.py CHANGED
@@ -31,71 +31,89 @@ dataset_name_YfOptions_output = os.getenv("dataset_name_YfOptions_output")
31
  HF_TOKEN_YfOptions = os.getenv("HF_TOKEN_YfOptions")
32
 
33
  # Set page configuration
34
- st.set_page_config(
35
- page_title="Option Data Screener App",
36
- page_icon="📊",
37
- layout="wide"
38
- )
39
 
40
 
41
  @st.cache_data
42
  def get_TD_DF(current_datetime):
43
  # Load lastest TradingView DataSet from HuggingFace Dataset which is always america.csv
44
  # download_from_hf_dataset("america.csv", "AmirTrader/TradingViewData", HF_TOKEN_YfOptions)
45
- DF = load_hf_dataset("america.csv", HF_TOKEN_YfOptions, dataset_name_TradingView_input)
 
 
46
 
47
  # get ticker list by filtering only above 1 billion dollar company
48
  # DF = pd.read_csv(f'america_2024-03-01.csv')
49
  tickerlst = list(DF.query("`Market Capitalization`>10e9").Ticker)
50
- # tickerlist = ['INDO', 'TSLA', 'AAPL', 'MSFT', 'GOOGL', 'AMZN', 'NFLX', 'META', 'NVDA', 'AMD', 'INTC', 'IBM', 'CSCO', 'ORCL', 'QCOM', 'TXN', 'AVGO', 'ADBE', 'CRM', 'NFLX', 'PYPL', 'SNAP']
51
  return DF, tickerlst
52
 
 
53
  @st.cache_data
54
  def get_options_DF(current_datetime):
55
- DF = load_hf_dataset("optionchain.csv", HF_TOKEN_YfOptions, dataset_name_YfOptions_output)
 
 
56
  return DF
57
 
 
58
  @st.cache_data
59
  def convert_df(df):
60
- return df.to_csv().encode('utf-8')
61
 
62
 
63
  @st.cache_data
64
  def get_options_merge(current_datetime):
65
-
66
  DF, tickerlst = get_TD_DF(current_datetime)
67
 
68
  DF_options_origin = get_options_DF(current_datetime)
69
-
70
- DF_options_origin['Volume_OpenInterest_Ratio'] = DF_options_origin['volume'] / DF_options_origin['openInterest']
71
 
72
- # Extract ticker from contractSymbol and merge dataframes
73
- DF_options_origin['Ticker'] = DF_options_origin['contractSymbol'].str.extract(r'([A-Z]+)')
74
- TD_interestedColumns = ['Ticker', 'Market Capitalization', 'Relative Volume']
75
- DF_options_merged = pd.merge(DF_options_origin, DF[TD_interestedColumns], on='Ticker', how='left')
76
 
 
 
 
 
 
 
 
 
77
 
78
  # Pivot the DataFrame to separate 'Call' and 'Put' for volume
79
- volume_pivot = DF_options_merged.groupby(['Ticker', 'Type'])['volume'].sum().unstack()
80
- volume_pivot.columns = ['Call_Volume', 'Put_Volume']
81
-
 
82
 
83
  # Pivot the DataFrame to separate 'Call' and 'Put' for openInterest
84
- openInterest_pivot = DF_options_merged.groupby(['Ticker', 'Type'])['openInterest'].sum().unstack()
85
- openInterest_pivot.columns = ['Call_openInterest', 'Put_openInterest']
 
 
86
 
87
  # Merge the volume and open interest DataFrames
88
- merged_df = volume_pivot.merge(openInterest_pivot, left_index=True, right_index=True)
 
 
89
 
90
  # Calculate Put/Call Volume Ratio
91
- merged_df['Put_Call_Volume_Ratio'] = merged_df['Put_Volume'] / merged_df['Call_Volume'] #.replace(0, pd.NA)
 
 
92
 
93
  # Calculate Put/Call Open Interest Ratio
94
- merged_df['Put_Call_OI_Ratio'] = merged_df['Put_openInterest'] / merged_df['Call_openInterest'] #.replace(0, pd.NA)
 
 
 
 
 
 
95
 
96
- DFtotal = pd.merge(DF_options_merged, merged_df, left_on='Ticker', right_index=True, how='left')
97
 
98
- return DFtotal, tickerlst
99
 
100
  DF_options, tickerlst = get_options_merge(current_datetime)
101
 
@@ -103,9 +121,9 @@ DF_options, tickerlst = get_options_merge(current_datetime)
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")
@@ -118,32 +136,46 @@ st.sidebar.markdown("### Filter Options Data")
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(
@@ -151,84 +183,97 @@ put_call_volume_range = st.sidebar.slider(
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")
231
- # **Daily Change in Open Interest**
232
  # Monitoring the increase or decrease in Open Interest compared to the previous day can indicate the inflow (rising OI) or outflow (declining OI) of capital. This factor helps assess the strength of the current trend.
233
  st.sidebar.button("Daily Change in Open Interest ")
234
  # contractSymbol
 
31
  HF_TOKEN_YfOptions = os.getenv("HF_TOKEN_YfOptions")
32
 
33
  # Set page configuration
34
+ st.set_page_config(page_title="Option Data Screener App", page_icon="📊", layout="wide")
 
 
 
 
35
 
36
 
37
  @st.cache_data
38
  def get_TD_DF(current_datetime):
39
  # Load lastest TradingView DataSet from HuggingFace Dataset which is always america.csv
40
  # download_from_hf_dataset("america.csv", "AmirTrader/TradingViewData", HF_TOKEN_YfOptions)
41
+ DF = load_hf_dataset(
42
+ "america.csv", HF_TOKEN_YfOptions, dataset_name_TradingView_input
43
+ )
44
 
45
  # get ticker list by filtering only above 1 billion dollar company
46
  # DF = pd.read_csv(f'america_2024-03-01.csv')
47
  tickerlst = list(DF.query("`Market Capitalization`>10e9").Ticker)
48
+
49
  return DF, tickerlst
50
 
51
+
52
  @st.cache_data
53
  def get_options_DF(current_datetime):
54
+ DF = load_hf_dataset(
55
+ "optionchain.csv", HF_TOKEN_YfOptions, dataset_name_YfOptions_output
56
+ )
57
  return DF
58
 
59
+
60
  @st.cache_data
61
  def convert_df(df):
62
+ return df.to_csv().encode("utf-8")
63
 
64
 
65
  @st.cache_data
66
  def get_options_merge(current_datetime):
 
67
  DF, tickerlst = get_TD_DF(current_datetime)
68
 
69
  DF_options_origin = get_options_DF(current_datetime)
 
 
70
 
71
+ DF_options_origin["Volume_OpenInterest_Ratio"] = (
72
+ DF_options_origin["volume"] / DF_options_origin["openInterest"]
73
+ )
 
74
 
75
+ # Extract ticker from contractSymbol and merge dataframes
76
+ DF_options_origin["Ticker"] = DF_options_origin["contractSymbol"].str.extract(
77
+ r"([A-Z]+)"
78
+ )
79
+ TD_interestedColumns = ["Ticker", "Market Capitalization", "Relative Volume"]
80
+ DF_options_merged = pd.merge(
81
+ DF_options_origin, DF[TD_interestedColumns], on="Ticker", how="left"
82
+ )
83
 
84
  # Pivot the DataFrame to separate 'Call' and 'Put' for volume
85
+ volume_pivot = (
86
+ DF_options_merged.groupby(["Ticker", "Type"])["volume"].sum().unstack()
87
+ )
88
+ volume_pivot.columns = ["Call_Volume", "Put_Volume"]
89
 
90
  # Pivot the DataFrame to separate 'Call' and 'Put' for openInterest
91
+ openInterest_pivot = (
92
+ DF_options_merged.groupby(["Ticker", "Type"])["openInterest"].sum().unstack()
93
+ )
94
+ openInterest_pivot.columns = ["Call_openInterest", "Put_openInterest"]
95
 
96
  # Merge the volume and open interest DataFrames
97
+ merged_df = volume_pivot.merge(
98
+ openInterest_pivot, left_index=True, right_index=True
99
+ )
100
 
101
  # Calculate Put/Call Volume Ratio
102
+ merged_df["Put_Call_Volume_Ratio"] = (
103
+ merged_df["Put_Volume"] / merged_df["Call_Volume"]
104
+ ) # .replace(0, pd.NA)
105
 
106
  # Calculate Put/Call Open Interest Ratio
107
+ merged_df["Put_Call_OI_Ratio"] = (
108
+ merged_df["Put_openInterest"] / merged_df["Call_openInterest"]
109
+ ) # .replace(0, pd.NA)
110
+
111
+ DFtotal = pd.merge(
112
+ DF_options_merged, merged_df, left_on="Ticker", right_index=True, how="left"
113
+ )
114
 
115
+ return DFtotal, tickerlst
116
 
 
117
 
118
  DF_options, tickerlst = get_options_merge(current_datetime)
119
 
 
121
  # Title
122
  st.title("📊 Unusual Options Activity Dashboard")
123
 
124
+ st.write(f"Number of avialable tickers: {len(tickerlst)}")
125
 
126
+ st.write(f"Number of options contract records: {len(DF_options)}")
127
 
128
  # Display options data
129
  st.header("Options Data")
 
136
  volume_range = st.sidebar.slider(
137
  "Volume Range",
138
  min_value=0,
139
+ max_value=DF_options["volume"].max().astype(int),
140
+ value=(0, DF_options["volume"].max().astype(int)),
141
+ step=100,
142
  )
143
 
144
  open_interest_range = st.sidebar.slider(
145
+ "Open Interest Range",
146
  min_value=0,
147
+ max_value=DF_options["openInterest"].max().astype(int),
148
+ value=(0, DF_options["openInterest"].max().astype(int)),
149
+ step=100,
150
  )
151
 
152
  # Add range selector for Volume/Open Interest Ratio
153
  vol_oi_ratio_range = st.sidebar.slider(
154
  "Volume/Open Interest Ratio Range",
155
  min_value=0.0,
156
+ max_value=100.0, #np.nanmax(
157
+ # DF_options["Volume_OpenInterest_Ratio"][
158
+ # ~np.isinf(DF_options["Volume_OpenInterest_Ratio"])
159
+ # ]
160
+ # ),
161
+ value=(0.0,10.0), #(
162
+ # 0.5,
163
+ # np.nanmax(
164
+ # DF_options["Volume_OpenInterest_Ratio"][
165
+ # ~np.isinf(DF_options["Volume_OpenInterest_Ratio"])
166
+ # ]
167
+ # )
168
+ # / 2,
169
+ # ),
170
+ step=0.1,
171
  )
172
 
173
  st.sidebar.markdown("---") # Add a horizontal line as a visual separator
174
 
175
  st.sidebar.markdown("### Filter Stock Data")
176
+ min_relative_volume = st.sidebar.number_input(
177
+ "Minimum Relative Volume", min_value=0.0, value=1.5, step=0.1
178
+ )
179
 
180
  # Changed to range sliders
181
  put_call_volume_range = st.sidebar.slider(
 
183
  min_value=0.0,
184
  max_value=10.0,
185
  value=(0.0, 0.6),
186
+ step=0.1,
187
  )
188
 
189
  put_call_oi_range = st.sidebar.slider(
190
+ "Put/Call OI Ratio Range", min_value=0.0, max_value=10.0, value=(0.0, 3.0), step=0.1
 
 
 
 
191
  )
192
 
193
+ # if st.sidebar.button("Filter"):
194
  # Filter the dataframe with the range
195
+ filtered_df = DF_options[
196
+ (DF_options["volume"] >= volume_range[0])
197
+ & (DF_options["volume"] <= volume_range[1])
198
+ & (DF_options["openInterest"] >= open_interest_range[0])
199
+ & (DF_options["openInterest"] <= open_interest_range[1])
200
+ & (DF_options["Relative Volume"] >= min_relative_volume)
201
+ & (DF_options["Put_Call_Volume_Ratio"] >= put_call_volume_range[0])
202
+ & (DF_options["Put_Call_Volume_Ratio"] <= put_call_volume_range[1])
203
+ & (DF_options["Put_Call_OI_Ratio"] >= put_call_oi_range[0])
204
+ & (DF_options["Put_Call_OI_Ratio"] <= put_call_oi_range[1])
205
+ & (DF_options["Volume_OpenInterest_Ratio"] >= vol_oi_ratio_range[0])
206
+ & (DF_options["Volume_OpenInterest_Ratio"] <= vol_oi_ratio_range[1])
207
+ ]
208
+
209
+ st.write(f"Filtered records: {len(filtered_df)} rows")
210
+
211
+ interestedColumns = [
212
+ "contractSymbol",
213
+ "volume",
214
+ "openInterest",
215
+ "impliedVolatility",
216
+ "Volume_OpenInterest_Ratio",
217
+ "Relative Volume",
218
+ "Put_Call_Volume_Ratio",
219
+ "Put_Call_OI_Ratio",
220
+ ]
221
+
222
+ selected_columns = st.multiselect(
223
+ "Select columns to display",
224
+ options=filtered_df.columns.tolist(),
225
+ default=interestedColumns,
226
+ )
227
 
228
+ if selected_columns:
229
+ st.dataframe(filtered_df[selected_columns])
230
 
231
  # Download button for the DataFrame
232
+ csv = convert_df(filtered_df)
233
+ st.download_button(
234
+ label="Download Options Data as CSV",
235
+ data=csv,
236
+ file_name=f"options_data_{current_datetime}.csv",
237
+ mime="text/csv",
238
+ )
 
 
239
 
240
+ st.write(f"Filtered Tickers: {filtered_df['Ticker'].unique()} ")
241
 
242
 
243
  st.sidebar.markdown("---") # Add a horizontal line as a visual separator
244
  st.sidebar.header("Ticker")
245
 
246
+ selectedTicker = st.sidebar.selectbox(
247
+ "Select Ticker",
248
+ options=tickerlst,
249
+ index=tickerlst.index("AAPL") if "AAPL" in tickerlst else 0,
250
+ )
251
 
252
  if st.sidebar.button("Submit"):
253
  st.write(f"Ticker {selectedTicker}")
254
  # filter DF_options for selectedTicker
255
+ filtered_ticker_df = DF_options[DF_options["Ticker"] == selectedTicker]
256
+ # plot plotly3D scatter plot for filtered_ticker_df for volume, openInterest and impliedVolatility
257
+ fig = px.scatter_3d(
258
+ filtered_ticker_df,
259
+ x="volume",
260
+ y="openInterest",
261
+ z="impliedVolatility",
262
+ color="Type",
263
+ title=f"3D Scatter Plot for {selectedTicker}",
264
+ hover_data=["strike", "lastPrice", "mark", "daysleft", "contractSymbol", "expirationDate"],
265
+ color_discrete_map={"CALL": "green", "PUT": "red"}
266
+ )
267
  fig.update_layout(
268
+ autosize=True, width=2560, height=600, margin=dict(l=50, r=50, b=50, t=50)
 
 
 
269
  )
270
+ st.plotly_chart(fig) # , use_container_width=True)
271
  # Display the filtered DataFrame
272
+
 
273
 
274
  st.sidebar.markdown("---") # Add a horizontal line as a visual separator
275
  st.sidebar.header("Advanced")
276
+ # **Daily Change in Open Interest**
277
  # Monitoring the increase or decrease in Open Interest compared to the previous day can indicate the inflow (rising OI) or outflow (declining OI) of capital. This factor helps assess the strength of the current trend.
278
  st.sidebar.button("Daily Change in Open Interest ")
279
  # contractSymbol