Spaces:
Running
Running
| # streamlit | |
| import streamlit as st | |
| import pandas as pd | |
| import numpy as np | |
| import plotly.express as px | |
| import os | |
| import time | |
| from dotenv import load_dotenv | |
| from datetime import datetime | |
| from utils import upload_to_hf_dataset, download_from_hf_dataset, load_hf_dataset | |
| # Get current date and time | |
| # current_datetime = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") | |
| current_datetime = datetime.now().strftime("%Y-%m-%d") | |
| # Load environment variables from .env file | |
| load_dotenv() | |
| # Get the name of the HuggingFace dataset for TradingView to read from | |
| dataset_name_TradingView_input = os.getenv("dataset_name_TradingView_input") | |
| # Get the name of the HuggingFace dataset for YfOptions to export | |
| dataset_name_YfOptions_output = os.getenv("dataset_name_YfOptions_output") | |
| # Get the Hugging Face API token from the environment; either set in .env file or in the environment directly in GitHub | |
| HF_TOKEN_YfOptions = os.getenv("HF_TOKEN_YfOptions") | |
| # Set page configuration | |
| st.set_page_config(page_title="Option Data Screener App", page_icon="📊", layout="wide") | |
| ######################################################################################################## | |
| # Functions | |
| def get_TD_DF(current_datetime): | |
| # Load lastest TradingView DataSet from HuggingFace Dataset which is always america.csv | |
| # download_from_hf_dataset("america.csv", "AmirTrader/TradingViewData", HF_TOKEN_YfOptions) | |
| DF = load_hf_dataset( | |
| "america.csv", HF_TOKEN_YfOptions, dataset_name_TradingView_input | |
| ) | |
| # get ticker list by filtering only above 1 billion dollar company | |
| # DF = pd.read_csv(f'america_2024-03-01.csv') | |
| tickerlst = list(DF.query("`Market Capitalization`>10e9").Ticker) | |
| return DF, tickerlst | |
| def get_options_DF(current_datetime): | |
| DF = load_hf_dataset( | |
| "optionchain.csv", HF_TOKEN_YfOptions, dataset_name_YfOptions_output | |
| ) | |
| return DF | |
| def convert_df(df): | |
| return df.to_csv().encode("utf-8") | |
| def convert_df_watchlist(df): | |
| return ",".join(map(str, df["Ticker"].unique())) | |
| def get_options_merge(current_datetime): | |
| DF, tickerlst = get_TD_DF(current_datetime) | |
| DF_options_origin = get_options_DF(current_datetime) | |
| # To safely compute the Volume_OpenInterest_Ratio in your DataFrame without encountering division by zero or null value errors, you can utilize pandas' div() method combined with appropriate handling for infinite and missing values. | |
| # DF_options_origin["Volume_OpenInterest_Ratio"] = ( | |
| # DF_options_origin["volume"] / DF_options_origin["openInterest"] | |
| # ) | |
| DF_options_origin["Volume_OpenInterest_Ratio"] = ( | |
| DF_options_origin["volume"] | |
| .div(DF_options_origin["openInterest"]) | |
| .replace([np.inf, -np.inf], 0) # Replace infinite values resulting from division by zero | |
| .fillna(0) # Replace NaN values resulting from division by nulls | |
| ) | |
| # Extract ticker from contractSymbol and merge dataframes | |
| DF_options_origin["Ticker"] = DF_options_origin["contractSymbol"].str.extract( | |
| r"([A-Z]+)" | |
| ) | |
| TD_interestedColumns = ["Ticker", "Market Capitalization", "Relative Volume"] | |
| DF_options_merged = pd.merge( | |
| DF_options_origin, DF[TD_interestedColumns], on="Ticker", how="left" | |
| ) | |
| # Pivot the DataFrame to separate 'Call' and 'Put' for volume | |
| volume_pivot = ( | |
| DF_options_merged.groupby(["Ticker", "Type"])["volume"].sum().unstack() | |
| ) | |
| volume_pivot.columns = ["Call_Volume", "Put_Volume"] | |
| # Pivot the DataFrame to separate 'Call' and 'Put' for openInterest | |
| openInterest_pivot = ( | |
| DF_options_merged.groupby(["Ticker", "Type"])["openInterest"].sum().unstack() | |
| ) | |
| openInterest_pivot.columns = ["Call_openInterest", "Put_openInterest"] | |
| # Merge the volume and open interest DataFrames | |
| merged_df = volume_pivot.merge( | |
| openInterest_pivot, left_index=True, right_index=True | |
| ) | |
| # Calculate Put/Call Volume Ratio | |
| merged_df["Put_Call_Volume_Ratio"] = ( | |
| merged_df["Put_Volume"] / merged_df["Call_Volume"] | |
| ) # .replace(0, pd.NA) | |
| # Calculate Put/Call Open Interest Ratio | |
| merged_df["Put_Call_OI_Ratio"] = ( | |
| merged_df["Put_openInterest"] / merged_df["Call_openInterest"] | |
| ) # .replace(0, pd.NA) | |
| DFtotal = pd.merge( | |
| DF_options_merged, merged_df, left_on="Ticker", right_index=True, how="left" | |
| ) | |
| DFtotal["Moneyvolume"] = DFtotal["lastPrice"] * DFtotal["volume"] *100 | |
| DFtotal["MoneyopenInterest"] = DFtotal["lastPrice"] * DFtotal["openInterest"] * 100 | |
| return DFtotal, tickerlst | |
| ######################################################################################################## | |
| # Main | |
| DF_options, tickerlst = get_options_merge(current_datetime) | |
| # Title | |
| st.title("📊 Unusual Options Activity Dashboard") | |
| st.write(f"Number of avialable tickers: **{len(tickerlst)}**") | |
| st.write(f"Number of options contract records: **{len(DF_options)}** with volume of **{round(DF_options['volume'].sum()/1e+6,2)}M** contracts and **{round(DF_options['openInterest'].sum()/1e+6,2)}M** open interest") | |
| if st.sidebar.button("Options Statistics", use_container_width=True): | |
| st.header("Statistics of Options Data") | |
| st.write(f'Value of Today volume options contracts: **{round(DF_options["Moneyvolume"].sum()/1e+9,2)}$B**') | |
| st.write(f'Value of open interest options contracts: **{round(DF_options["MoneyopenInterest"].sum()/1e+9,2)}$B**') | |
| st.write(f'Maximum Volume {int( DF_options["volume"].max())} beloing to Ticker **{DF_options.loc[DF_options["volume"].idxmax(), "Ticker"]}** and contractSymbol {DF_options.loc[DF_options["volume"].idxmax(), "contractSymbol"]}') | |
| st.write(f'Maximum Open Interest { int(DF_options["openInterest"].max())} beloing to Ticker **{DF_options.loc[DF_options["openInterest"].idxmax(), "Ticker"]}** and contractSymbol {DF_options.loc[DF_options["openInterest"].idxmax(), "contractSymbol"]}') | |
| st.write(f'Maximum Implied Volatility { round(DF_options["impliedVolatility"].max(),2)} beloing to Ticker **{DF_options.loc[DF_options["impliedVolatility"].idxmax(), "Ticker"]}**') | |
| st.write(f'Maximum Volume/Open Interest Ratio { DF_options["Volume_OpenInterest_Ratio"].max()} beloing to Ticker {DF_options.loc[DF_options["Volume_OpenInterest_Ratio"].idxmax(), "Ticker"]}') | |
| st.write(f'Maximum Relative Volume { round(DF_options["Relative Volume"].max(),2)} beloing to Ticker {DF_options.loc[DF_options["Relative Volume"].idxmax(), "Ticker"]}') | |
| # plot top 10 volume and open interest; x is Ticker, y is volume and open interest | |
| fig = px.bar( | |
| DF_options.nlargest(100, "volume"), | |
| x="Ticker", | |
| y="volume", | |
| color="Type", | |
| title="Top Options by Volume", | |
| color_discrete_map={"CALL": "green", "PUT": "red"}, | |
| ) | |
| fig.update_layout( | |
| xaxis_title="Ticker", | |
| yaxis_title="Volume", | |
| autosize=True, | |
| height=600, | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| fig = px.bar( | |
| DF_options.nlargest(100, "openInterest"), | |
| x="Ticker", | |
| y="openInterest", | |
| color="Type", | |
| title="Top Options by Open Interest", | |
| color_discrete_map={"CALL": "green", "PUT": "red"}, | |
| ) | |
| fig.update_layout( | |
| xaxis_title="Ticker", | |
| yaxis_title="Open Interest", | |
| autosize=True, | |
| height=600, | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| # Sidebar | |
| st.sidebar.header("Controls") | |
| st.sidebar.markdown("### Filter Options Data") | |
| # Change number inputs to range sliders for volume and open interest | |
| volume_range = st.sidebar.slider( | |
| "Volume Range", | |
| min_value=0, | |
| max_value=DF_options["volume"].max().astype(int), | |
| value=(0, DF_options["volume"].max().astype(int)), | |
| step=100, | |
| ) | |
| open_interest_range = st.sidebar.slider( | |
| "Open Interest Range", | |
| min_value=0, | |
| max_value=DF_options["openInterest"].max().astype(int), | |
| value=(0, DF_options["openInterest"].max().astype(int)), | |
| step=100, | |
| ) | |
| # Add range selector for Volume/Open Interest Ratio | |
| vol_oi_ratio_range = st.sidebar.slider( | |
| "Volume/Open Interest Ratio Range", | |
| min_value=0.0, | |
| max_value= np.nanmax( | |
| DF_options["Volume_OpenInterest_Ratio"][ | |
| ~np.isinf(DF_options["Volume_OpenInterest_Ratio"]) | |
| ] | |
| ), | |
| value=(0.0, 10.0), # ( | |
| # 0.5, | |
| # np.nanmax( | |
| # DF_options["Volume_OpenInterest_Ratio"][ | |
| # ~np.isinf(DF_options["Volume_OpenInterest_Ratio"]) | |
| # ] | |
| # ) | |
| # / 2, | |
| # ), | |
| step=0.1, | |
| ) | |
| # Add expiration date filter | |
| expiration_dates = sorted(DF_options["expirationDate"].unique()) | |
| selected_expiry = st.sidebar.multiselect( | |
| "Select Expiration Dates", | |
| options=expiration_dates, | |
| default=expiration_dates[:4], # Default to first 3 dates | |
| ) | |
| st.sidebar.markdown("---") # Add a horizontal line as a visual separator | |
| st.sidebar.markdown("### Filter Stock Data") | |
| min_relative_volume = st.sidebar.number_input( | |
| "Minimum Relative Volume", min_value=0.0, value=1.5, step=0.1 | |
| ) | |
| # Changed to range sliders | |
| put_call_volume_range = st.sidebar.slider( | |
| "Put/Call Volume Ratio Range", | |
| min_value=0.0, | |
| max_value=np.nanmax( | |
| DF_options["Put_Call_Volume_Ratio"][ | |
| ~np.isinf(DF_options["Put_Call_Volume_Ratio"]) | |
| ] | |
| ), | |
| value=(0.0, 0.6), | |
| step=0.1, | |
| ) | |
| put_call_oi_range = st.sidebar.slider( | |
| "Put/Call OI Ratio Range", | |
| min_value=0.0, | |
| max_value=np.nanmax( | |
| DF_options["Put_Call_OI_Ratio"][~np.isinf(DF_options["Put_Call_OI_Ratio"])] | |
| ), | |
| value=(0.0, 3.0), | |
| step=0.1, | |
| ) | |
| if st.sidebar.button("Filter", use_container_width=True): | |
| # Display options data | |
| st.header("Filtering Options Data") | |
| # Filter the dataframe with the range and expiry dates | |
| filtered_df = DF_options[ | |
| (DF_options["volume"] >= volume_range[0]) | |
| & (DF_options["volume"] <= volume_range[1]) | |
| & (DF_options["openInterest"] >= open_interest_range[0]) | |
| & (DF_options["openInterest"] <= open_interest_range[1]) | |
| & (DF_options["Relative Volume"] >= min_relative_volume) | |
| & (DF_options["Put_Call_Volume_Ratio"] >= put_call_volume_range[0]) | |
| & (DF_options["Put_Call_Volume_Ratio"] <= put_call_volume_range[1]) | |
| & (DF_options["Put_Call_OI_Ratio"] >= put_call_oi_range[0]) | |
| & (DF_options["Put_Call_OI_Ratio"] <= put_call_oi_range[1]) | |
| & (DF_options["Volume_OpenInterest_Ratio"] >= vol_oi_ratio_range[0]) | |
| & (DF_options["Volume_OpenInterest_Ratio"] <= vol_oi_ratio_range[1]) | |
| & (DF_options["expirationDate"].isin(selected_expiry)) | |
| ] | |
| st.write(f"Filtered records: {len(filtered_df)} rows") | |
| interestedColumns = [ | |
| "contractSymbol", | |
| "expirationDate", | |
| "volume", | |
| "openInterest", | |
| "impliedVolatility", | |
| "Volume_OpenInterest_Ratio", | |
| "Relative Volume", | |
| "Put_Call_Volume_Ratio", | |
| "Put_Call_OI_Ratio", | |
| ] | |
| # selected_columns = st.sidebar.multiselect( | |
| # "Select columns to display", | |
| # options=DF_options.columns.tolist(), | |
| # default=interestedColumns, | |
| # ) | |
| if interestedColumns: | |
| st.dataframe(filtered_df[interestedColumns].reset_index(drop=True)) | |
| # Download button for the DataFrame | |
| csv = convert_df(filtered_df) | |
| st.download_button( | |
| label="Download Options Data as CSV", | |
| data=csv, | |
| file_name=f"options_data_{current_datetime}.csv", | |
| mime="text/csv", | |
| ) | |
| csv = convert_df_watchlist(filtered_df) | |
| st.download_button( | |
| label="Download Stock WatchList for TradingView", | |
| data=csv, | |
| file_name=f"filtered_options_data_{current_datetime}.txt", | |
| mime="text/csv", | |
| ) | |
| st.write(f"Unique Filtered Tickers: **{csv}** ") | |
| st.sidebar.markdown("---") # Add a horizontal line as a visual separator | |
| st.sidebar.header("Ticker") | |
| selectedTicker = st.sidebar.selectbox( | |
| "Select Ticker", | |
| options=tickerlst, | |
| index=tickerlst.index("NVDA") if "NVDA" in tickerlst else 0, | |
| ) | |
| if st.sidebar.button("Option Chain Visualization", use_container_width=True): | |
| st.header(f"Options Chain Visualization for Ticker {selectedTicker}") | |
| # filter DF_options for selectedTicker | |
| filtered_ticker_df = DF_options[DF_options["Ticker"] == selectedTicker] | |
| # Create two columns for the charts | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| fig_3d = px.scatter_3d( | |
| filtered_ticker_df, | |
| x="volume", | |
| y="openInterest", | |
| z="impliedVolatility", | |
| color="Type", | |
| title=f"3D Scatter Plot for {selectedTicker}", | |
| hover_data=[ | |
| "strike", | |
| "lastPrice", | |
| "mark", | |
| "daysleft", | |
| "contractSymbol", | |
| "expirationDate", | |
| ], | |
| color_discrete_map={"CALL": "green", "PUT": "red"}, | |
| ) | |
| fig_3d.update_layout( | |
| autosize=True, height=600, margin=dict(l=50, r=50, b=50, t=50) | |
| ) | |
| st.plotly_chart(fig_3d, use_container_width=True) | |
| with col2: | |
| # 3D Volatility Surface: Implied Volatility by Strike and Days to Expiration | |
| fig_surface = px.scatter_3d( | |
| filtered_ticker_df, | |
| x="strike", | |
| y="daysleft", | |
| z="impliedVolatility", | |
| color="Type", | |
| title=f"Implied Volatility Surface for {selectedTicker}", | |
| hover_data=[ | |
| "volume", | |
| "openInterest", | |
| "lastPrice", | |
| "mark", | |
| "contractSymbol", | |
| ], | |
| color_discrete_map={"CALL": "green", "PUT": "red"}, | |
| ) | |
| fig_surface.update_traces(marker=dict(size=5)) | |
| fig_surface.update_layout( | |
| scene=dict( | |
| xaxis_title="Strike Price", | |
| yaxis_title="Days to Expiration", | |
| zaxis_title="Implied Volatility", | |
| ), | |
| autosize=True, | |
| height=600, | |
| margin=dict(l=50, r=50, b=50, t=50), | |
| ) | |
| st.plotly_chart(fig_surface, use_container_width=True) | |
| with col3: | |
| # 3D Surface Plot: Option Price vs. Strike Price and Days to Expiration | |
| fig_surface2 = px.scatter_3d( | |
| filtered_ticker_df, | |
| x="strike", | |
| y="daysleft", | |
| z="lastPrice", | |
| color="Type", | |
| title=f"Option Price Surface for {selectedTicker}", | |
| hover_data=[ | |
| "volume", | |
| "openInterest", | |
| "impliedVolatility", | |
| "mark", | |
| "contractSymbol", | |
| ], | |
| color_discrete_map={"CALL": "green", "PUT": "red"}, | |
| ) | |
| fig_surface2.update_traces(marker=dict(size=5)) | |
| fig_surface2.update_layout( | |
| scene=dict( | |
| xaxis_title="Strike Price", | |
| yaxis_title="Days to Expiration", | |
| zaxis_title="Option Price", | |
| ), | |
| autosize=True, | |
| height=600, | |
| margin=dict(l=50, r=50, b=50, t=50), | |
| ) | |
| st.plotly_chart(fig_surface2, use_container_width=True) | |
| # Display the filtered DataFrame | |
| st.dataframe( | |
| filtered_ticker_df.query("Ticker ==@selectedTicker")[ | |
| [ | |
| "contractSymbol", | |
| "daysleft", | |
| "Type", | |
| "strike", | |
| "lastPrice", | |
| "volume", | |
| "openInterest", | |
| "impliedVolatility", | |
| "inTheMoney", | |
| ] | |
| ].reset_index(drop=True), | |
| use_container_width=True, | |
| hide_index=True, | |
| height=600, | |
| ) | |
| # Create two columns for the charts | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| # Bar chart for Volume by Strike Price | |
| fig_bar = px.bar( | |
| filtered_ticker_df, | |
| x="strike", | |
| y="volume", | |
| color="Type", | |
| title=f"Volume by Strike Price for {selectedTicker}", | |
| barmode="group", | |
| color_discrete_map={"CALL": "green", "PUT": "red"}, | |
| ) | |
| fig_bar.update_layout( | |
| xaxis_title="Strike Price", yaxis_title="Volume", autosize=True, height=600 | |
| ) | |
| st.plotly_chart(fig_bar, use_container_width=True) | |
| with col2: | |
| # Bar chart for Open Interest by Expiration Date | |
| fig_bar2 = px.bar( | |
| filtered_ticker_df, | |
| x="expirationDate", | |
| y="openInterest", | |
| color="Type", | |
| title=f"Open Interest by Expiration Date for {selectedTicker}", | |
| barmode="group", | |
| color_discrete_map={"CALL": "green", "PUT": "red"}, | |
| ) | |
| fig_bar.update_layout( | |
| xaxis_title="Expiration Date", | |
| yaxis_title="Open Interest", | |
| autosize=True, | |
| height=600, | |
| ) | |
| st.plotly_chart(fig_bar2, use_container_width=True) | |
| with col3: | |
| # mplied Volatility by Strike Price | |
| fig_bar3 = px.bar( | |
| filtered_ticker_df, | |
| x="strike", | |
| y="impliedVolatility", | |
| color="Type", | |
| title=f"Implied Volatility by Strike Price for {selectedTicker}", | |
| barmode="group", | |
| color_discrete_map={"CALL": "green", "PUT": "red"}, | |
| ) | |
| st.plotly_chart(fig_bar3, use_container_width=True) | |
| st.write( | |
| "*Tips: in Open Interest by Strike Bar Chart, High call OI at a particular strike may indicate a resistance level, while high put OI may suggest support!*" | |
| ) | |
| # plot Open Interest by Strike Bar ChartFilter DF_options for soon-to-expire options less than 14 days | |
| filtered_ticker_df_soon = filtered_ticker_df[filtered_ticker_df["daysleft"] <= 14] | |
| fig_bar4 = px.bar( | |
| filtered_ticker_df_soon, | |
| x="strike", | |
| y="openInterest", | |
| color="Type", | |
| title=f"Open Interest by Strike Price for {selectedTicker}", | |
| barmode="group", | |
| color_discrete_map={"CALL": "green", "PUT": "red"}, | |
| ) | |
| fig_bar4.update_layout( | |
| xaxis_title="Strike Price", | |
| yaxis_title="Open Interest", | |
| autosize=True, | |
| height=600, | |
| ) | |
| st.plotly_chart(fig_bar4, use_container_width=True) | |
| # Display the filtered DataFrame | |
| st.markdown("---") # Add a horizontal line as a visual separator | |
| st.write( | |
| "*Tips: in Open Interest Heatmap visualizes where significant open interest concentrations exist across different strikes and expirations." | |
| ) | |
| fig_bar5 = px.density_heatmap( | |
| filtered_ticker_df_soon, | |
| x="strike", | |
| y="expirationDate", | |
| z="openInterest", | |
| color_continuous_scale="Viridis", | |
| title=f"Open Interest Heatmap for {selectedTicker}", | |
| labels={"x": "Strike Price", "y": "Expiration Date", "z": "Open Interest"}, | |
| ) | |
| fig_bar5.update_layout( | |
| xaxis_title="Strike Price", | |
| yaxis_title="Expiration Date", | |
| autosize=True, | |
| height=600, | |
| ) | |
| st.plotly_chart(fig_bar5, use_container_width=True) | |
| # Combined OI and Volume Chart | |
| # Description: Overlay line plots of open interest and volume for each strike price. | |
| # Purpose: Identifies strikes with both high open interest and volume, indicating active trading zones. | |
| # Implementation: Aggregate openInterest and volume by strike. | |
| # Plot both metrics on the same chart using different y-axes or colors for clarity. | |
| st.markdown("---") # Add a horizontal line as a visual separator | |
| st.write( | |
| "*Tips: in Combined Open Interest and Volume Chart, High open interest with high volume may indicate strong interest in that strike price.*" | |
| ) | |
| fig_bar6 = px.line( | |
| filtered_ticker_df_soon, | |
| x="strike", | |
| y=["openInterest", "volume"], | |
| title=f"Combined Open Interest and Volume for {selectedTicker}", | |
| labels={"value": "Value", "variable": "Metric"}, | |
| ) | |
| fig_bar6.update_layout( | |
| xaxis_title="Strike Price", | |
| yaxis_title="Value", | |
| autosize=True, | |
| height=600, | |
| ) | |
| st.plotly_chart(fig_bar6, use_container_width=True) | |
| st.markdown("---") # Add a horizontal line as a visual separator | |
| # Price vs. Strike Scatter Plot with OI Sizing | |
| # Description: Scatter plot where each point represents an option contract, with the x-axis as strike price, y-axis as option price, and point size proportional to open interest. | |
| # Purpose: Visualizes the relationship between option pricing and open interest across strikes. | |
| # Implementation: Use strike for the x-axis and lastPrice for the y-axis. Set the size of each point based on openInterest. Differentiate calls and puts using color or markers | |
| st.write( | |
| "*Tips: in Price vs. Strike Scatter Plot with OI Sizing, Larger points indicate higher open interest, helping to identify popular strike prices.*" | |
| ) | |
| fig_bar7 = px.scatter( | |
| filtered_ticker_df_soon, | |
| x="strike", | |
| y="lastPrice", | |
| size="openInterest", | |
| color="Type", | |
| title=f"Price vs. Strike Scatter Plot for {selectedTicker}", | |
| labels={"x": "Strike Price", "y": "Option Price"}, | |
| color_discrete_map={"CALL": "green", "PUT": "red"}, | |
| ) | |
| fig_bar7.update_layout( | |
| xaxis_title="Strike Price", | |
| yaxis_title="Option Price", | |
| autosize=True, | |
| height=600, | |
| ) | |
| st.plotly_chart(fig_bar7, use_container_width=True) | |
| st.markdown("---") # Add a horizontal line as a visual separator | |
| # Price vs. Strike Scatter Plot with OI Sizing | |
| # Description: Scatter plot where each point represents an option contract, with the x-axis as strike price, y-axis as option price, and point size proportional to open interest. | |
| # Purpose: Visualizes the relationship between option pricing and open interest across strikes. | |
| # Implementation: Use strike for the x-axis and lastPrice for the y-axis. Set the size of each point based on openInterest. Differentiate calls and puts using color or markers | |
| st.write( | |
| "*Tips: in Price vs. Strike Scatter Plot with Days Left Sizing, Larger points indicate higher open interest, helping to identify popular strike prices.*" | |
| ) | |
| fig_bar7_2 = px.scatter( | |
| filtered_ticker_df, | |
| x="strike", | |
| y="lastPrice", | |
| size="daysleft", | |
| color="Type", | |
| title=f"Price vs. Strike Scatter Plot for {selectedTicker}", | |
| labels={"x": "Strike Price", "y": "Option Price"}, | |
| color_discrete_map={"CALL": "green", "PUT": "red"}, | |
| ) | |
| fig_bar7_2.update_layout( | |
| xaxis_title="Strike Price", | |
| yaxis_title="Option Price", | |
| autosize=True, | |
| height=600, | |
| ) | |
| st.plotly_chart(fig_bar7_2, use_container_width=True) | |
| st.markdown("---") # Add a horizontal line as a visual separator | |
| # Support and Resistance Levels on Price Chart | |
| # Description: Overlay horizontal lines on the underlying asset's price chart at strike prices with significant open interest. | |
| # Purpose: Directly correlates high OI strikes with potential support and resistance levels on the price chart. | |
| # Implementation: Identify top N strikes with highest call and put open interest. Plot the underlying asset's price chart. | |
| # Add horizontal lines at the identified strike prices, labeling them as support or resistance. | |
| st.write( | |
| "*Tips: in Support and Resistance Levels on Price Chart, High call OI at a particular strike may indicate a resistance level, while high put OI may suggest support!*" | |
| ) | |
| fig_bar8 = px.line( | |
| filtered_ticker_df_soon, | |
| x="strike", | |
| y="lastPrice", | |
| title=f"Support and Resistance Levels for {selectedTicker}", | |
| labels={"x": "Strike Price", "y": "Option Price"}, | |
| ) | |
| fig_bar8.update_layout( | |
| xaxis_title="Strike Price", | |
| yaxis_title="Option Price", | |
| autosize=True, | |
| height=600, | |
| ) | |
| st.plotly_chart(fig_bar8, use_container_width=True) | |
| st.sidebar.markdown("---") # Add a horizontal line as a visual separator | |
| st.sidebar.header("Advanced (Placeholder)") | |
| # **Daily Change in Open Interest** | |
| # 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. | |
| if st.sidebar.button("Daily Change in Open Interest ", use_container_width=True): | |
| st.write("Daily Change in Open Interest - Shows the change in open interest") | |
| st.info("Daily change in open interest analysis coming soon...") | |
| if st.sidebar.button("Gamma Squeeze", use_container_width=True): | |
| st.write( | |
| "Gamma Squeeze - Shows the potential for a rapid price movement in the underlying asset" | |
| ) | |
| st.info("Gamma squeeze analysis coming soon...") | |
| # Add buttons for each analysis type | |
| if st.sidebar.button("Vanna Exposure Analysis", use_container_width=True): | |
| st.write( | |
| "Vanna Exposure Analysis - Shows sensitivity of delta to volatility changes" | |
| ) | |
| # TODO: Implement Vanna exposure calculation and visualization | |
| # Would require calculating vanna values from the options data | |
| st.info("Vanna exposure analysis coming soon...") | |
| if st.sidebar.button("Charm Analysis", use_container_width=True): | |
| st.write("Charm Analysis - Shows rate of change of delta with respect to time") | |
| # TODO: Implement Charm calculation and visualization | |
| # Would require calculating charm values from the options data | |
| st.info("Charm analysis coming soon...") | |
| if st.sidebar.button("Gamma Exposure Analysis", use_container_width=True): | |
| st.write( | |
| "Gamma Exposure Analysis - Shows how delta changes with underlying price movement" | |
| ) | |
| # TODO: Implement gamma exposure calculation and visualization | |
| filtered_gamma = DF_options[DF_options["Ticker"] == selectedTicker] | |
| st.info("Gamma exposure analysis coming soon...") | |
| # contractSymbol | |
| # lastTradeDate | |
| # strike | |
| # lastPrice | |
| # bid | |
| # ask | |
| # change | |
| # percentChange | |
| # volume | |
| # openInterest | |
| # impliedVolatility | |
| # inTheMoney | |
| # contractSize | |
| # currency | |
| # Type | |
| # expirationDate | |
| # daysleft | |
| # mark | |
| # pricepercent | |
| # pricepercentstrike | |
| # interinsicvalue | |
| # interinsicvalue% | |
| # timevalue | |
| # timevalue% | |
| # breakevenprice | |
| # # Create sample data | |
| # np.random.seed(42) | |
| # data = pd.DataFrame({ | |
| # 'x': np.random.randn(100), | |
| # 'y': np.random.randn(100), | |
| # 'category': np.random.choice(['A', 'B', 'C'], 100) | |
| # }) | |
| # # Create two columns | |
| # col1, col2 = st.columns(2) | |
| # # First column - Scatter plot | |
| # with col1: | |
| # st.subheader("Scatter Plot") | |
| # fig = px.scatter(data, x='x', y='y', color='category') | |
| # st.plotly_chart(fig, use_container_width=True) | |
| # # Second column - Bar chart | |
| # with col2: | |
| # st.subheader("Bar Chart") | |
| # category_counts = data['category'].value_counts() | |
| # fig = px.bar(x=category_counts.index, y=category_counts.values) | |
| # st.plotly_chart(fig, use_container_width=True) | |
| # # Add a checkbox | |
| # if st.checkbox("Show raw data"): | |
| # st.dataframe(data) | |
| # # Add a download button | |
| # @st.cache_data | |
| # def convert_df(df): | |
| # return df.to_csv().encode('utf-8') | |
| # csv = convert_df(data) | |
| # st.download_button( | |
| # label="Download data as CSV", | |
| # data=csv, | |
| # file_name='sample_data.csv', | |
| # mime='text/csv', | |
| # ) | |