File size: 27,450 Bytes
c83b2fb 8667e7c c83b2fb b835338 c83b2fb a023d86 c83b2fb b835338 c83b2fb b835338 c83b2fb b835338 c83b2fb b835338 c83b2fb b835338 c83b2fb b835338 c83b2fb a023d86 c83b2fb eba68e6 b835338 eba68e6 c83b2fb b835338 c83b2fb b835338 c83b2fb b835338 c83b2fb b835338 c83b2fb b835338 c83b2fb b835338 eba68e6 b835338 c83b2fb a023d86 c83b2fb 8667e7c c83b2fb eba68e6 63566b8 b12ed54 eba68e6 c83b2fb 8667e7c b835338 8667e7c b835338 8667e7c b835338 8667e7c eba68e6 a023d86 b835338 8667e7c c83b2fb 42c5b25 c83b2fb b835338 c83b2fb 8667e7c 42c5b25 8667e7c b835338 8667e7c c83b2fb 8667e7c 42c5b25 c83b2fb 42c5b25 eba68e6 3a4a122 a023d86 eba68e6 3a4a122 a023d86 3a4a122 42c5b25 a023d86 c83b2fb a023d86 c83b2fb 8667e7c b835338 42c5b25 b835338 8667e7c 42c5b25 eba68e6 8667e7c b835338 a023d86 8667e7c a023d86 b835338 42c5b25 a667e70 42c5b25 c83b2fb 42c5b25 b835338 c83b2fb 42c5b25 c83b2fb |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 |
# 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
@st.cache_data
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
@st.cache_data
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()))
@st.cache_data
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',
# )
|