|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
current_datetime = datetime.now().strftime("%Y-%m-%d") |
|
|
|
|
|
|
|
|
|
|
|
load_dotenv() |
|
|
|
|
|
|
|
|
dataset_name_TradingView_input = os.getenv("dataset_name_TradingView_input") |
|
|
|
|
|
|
|
|
dataset_name_YfOptions_output = os.getenv("dataset_name_YfOptions_output") |
|
|
|
|
|
|
|
|
HF_TOKEN_YfOptions = os.getenv("HF_TOKEN_YfOptions") |
|
|
|
|
|
|
|
|
st.set_page_config( |
|
|
page_title="Option Data Screener App", |
|
|
page_icon="📊", |
|
|
layout="wide" |
|
|
) |
|
|
|
|
|
|
|
|
@st.cache_data |
|
|
def get_TD_DF(current_datetime): |
|
|
|
|
|
|
|
|
DF = load_hf_dataset("america.csv", HF_TOKEN_YfOptions, dataset_name_TradingView_input) |
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
@st.cache_data |
|
|
def convert_df(df): |
|
|
return df.to_csv().encode('utf-8') |
|
|
|
|
|
|
|
|
@st.cache_data |
|
|
def get_options_merge(current_datetime): |
|
|
|
|
|
DF, tickerlst = get_TD_DF(current_datetime) |
|
|
|
|
|
DF_options_origin = get_options_DF(current_datetime) |
|
|
|
|
|
DF_options_origin['Volume_OpenInterest_Ratio'] = DF_options_origin['volume'] / DF_options_origin['openInterest'] |
|
|
|
|
|
|
|
|
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') |
|
|
|
|
|
|
|
|
|
|
|
volume_pivot = DF_options_merged.groupby(['Ticker', 'Type'])['volume'].sum().unstack() |
|
|
volume_pivot.columns = ['Call_Volume', 'Put_Volume'] |
|
|
|
|
|
|
|
|
|
|
|
openInterest_pivot = DF_options_merged.groupby(['Ticker', 'Type'])['openInterest'].sum().unstack() |
|
|
openInterest_pivot.columns = ['Call_openInterest', 'Put_openInterest'] |
|
|
|
|
|
|
|
|
merged_df = volume_pivot.merge(openInterest_pivot, left_index=True, right_index=True) |
|
|
|
|
|
|
|
|
merged_df['Put_Call_Volume_Ratio'] = merged_df['Put_Volume'] / merged_df['Call_Volume'] |
|
|
|
|
|
|
|
|
merged_df['Put_Call_OI_Ratio'] = merged_df['Put_openInterest'] / merged_df['Call_openInterest'] |
|
|
|
|
|
DFtotal = pd.merge(DF_options_merged, merged_df, left_on='Ticker', right_index=True, how='left') |
|
|
|
|
|
return DFtotal, tickerlst |
|
|
|
|
|
DF_options, tickerlst = get_options_merge(current_datetime) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
st.title("📊 Options Data Dashboard") |
|
|
|
|
|
|
|
|
|
|
|
st.write(f'Number of avialable tickers: {len(tickerlst)}') |
|
|
|
|
|
|
|
|
st.write(f'Number of options records: {len(DF_options)}') |
|
|
|
|
|
|
|
|
st.header("Options Data") |
|
|
|
|
|
|
|
|
st.sidebar.header("Controls") |
|
|
st.sidebar.markdown("### Filter Options Data") |
|
|
|
|
|
min_volume = st.sidebar.number_input("Minimum Volume", min_value=0, value=100) |
|
|
min_open_interest = st.sidebar.number_input("Minimum Open Interest", min_value=0, value=100) |
|
|
min_vol_oi_ratio = st.sidebar.number_input("Minimum Volume/Open Interest Ratio", min_value=0.0, value=0.5, step=0.1) |
|
|
|
|
|
|
|
|
st.sidebar.markdown("---") |
|
|
|
|
|
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) |
|
|
min_put_call_volume = st.sidebar.number_input("Minimum Put/Call Volume Ratio", min_value=0.0, value=0.0, step=0.1) |
|
|
min_put_call_oi = st.sidebar.number_input("Minimum Put/Call OI Ratio", min_value=0.0, value=0.0, step=0.1) |
|
|
|
|
|
|
|
|
filtered_df = DF_options[ |
|
|
(DF_options['volume'] >= min_volume) & |
|
|
(DF_options['openInterest'] >= min_open_interest) & |
|
|
(DF_options['Relative Volume'] >= min_relative_volume) & |
|
|
(DF_options['Put_Call_Volume_Ratio'] >= min_put_call_volume) & |
|
|
(DF_options['Put_Call_OI_Ratio'] >= min_put_call_oi) & |
|
|
(DF_options['Volume_OpenInterest_Ratio'] >= min_vol_oi_ratio) |
|
|
] |
|
|
|
|
|
st.write(f"Filtered records: {len(filtered_df)} rows") |
|
|
|
|
|
|
|
|
interestedColumns = ['contractSymbol' , 'volume', 'openInterest', 'impliedVolatility', 'Volume_OpenInterest_Ratio' , 'Relative Volume' , 'Put_Call_Volume_Ratio' , 'Put_Call_OI_Ratio' , ] |
|
|
|
|
|
selected_columns = st.multiselect( |
|
|
"Select columns to display", |
|
|
options=filtered_df.columns.tolist(), |
|
|
default=interestedColumns |
|
|
) |
|
|
|
|
|
if selected_columns: |
|
|
st.dataframe(filtered_df[selected_columns]) |
|
|
|
|
|
|
|
|
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', |
|
|
) |
|
|
|
|
|
st.write(f"Filtered Tickers: {filtered_df['Ticker'].unique()} ") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
st.sidebar.markdown("---") |
|
|
st.sidebar.header("Advanced") |
|
|
|
|
|
|
|
|
st.sidebar.button("Daily Change in Open Interest ") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|