import streamlit as st import requests import pandas as pd import altair as alt # Function to fetch data from the API def fetch_data(url, headers): response = requests.get(url, headers=headers) if response.status_code == 200: return response.json()['data'] else: st.error("Failed to fetch data") return [] # Altair chart for combined volume and average price def create_chart(df, option_type, strike): # Ensure datetime is correctly parsed and localized df['tape_time'] = pd.to_datetime(df['tape_time']).dt.tz_localize(None) # Volume chart with custom colors for bid, ask, and mid volumes volume_chart = alt.Chart(df).transform_fold( fold=['bid_volume', 'ask_volume', 'mid_volume'], as_=['Volume Type', 'Volume'] ).mark_bar().encode( x='tape_time:T', y=alt.Y('Volume:Q', title='Volume'), color=alt.Color('Volume Type:N', scale=alt.Scale(domain=['bid_volume', 'ask_volume', 'mid_volume'], range=['#ee5365', '#1fb386', '#2448fb'])), tooltip=['tape_time:T', 'bid_volume:Q', 'ask_volume:Q', 'mid_volume:Q'] ).properties( width=300, height=300 ) # Average price line chart price_chart = alt.Chart(df).mark_line(color='#ffa421').encode( x='tape_time:T', y=alt.Y('avg_price:Q', title='Average Price', scale=alt.Scale(zero=False)), tooltip=['tape_time:T', 'avg_price:Q'] ) # Implied Volatility High line chart iv_high_chart = alt.Chart(df).mark_line(color='purple').encode( x='tape_time:T', y=alt.Y('iv_high:Q', title='IV High', scale=alt.Scale(zero=False)), tooltip=['tape_time:T', 'iv_high:Q'] ) # Combine the charts combined_chart = alt.layer(volume_chart, price_chart, iv_high_chart).resolve_scale( y='independent' ).properties( title=f"{option_type} Option Chart for Strike {strike}", width=600, height=300 ) return combined_chart # Streamlit UI st.title('Stacked Market Data Chart for Calls and Puts') with st.form(key='data_form'): index = st.text_input('Index (e.g., SPXW)', value='SPXW') expiry = st.date_input('Expiry Date') atm_strike = st.number_input('ATM Strike (e.g., 52350)', value=52350) submit_button = st.form_submit_button('Load Charts') if submit_button: headers = { "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br, zstd", "Accept-Language": "en-IN,en-DE;q=0.9,en;q=0.8,de-DE;q=0.7,de;q=0.6,en-US;q=0.5,en-GB;q=0.4,ga;q=0.3", "Authorization": "Bearer Qo2zhp0Xf3qI88x81tEvgizT_fH29PlLqhrnRcMHP8gvZ_5OCjWl_r23GdbBzolL", "Content-Type": "application/json", "Origin": "https://unusualwhales.com", "Referer": "https://unusualwhales.com/", "Sec-Ch-Ua": '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"', "Sec-Ch-Ua-Mobile": "?0", "Sec-Ch-Ua-Platform": '"Windows"', "Sec-Fetch-Dest": "empty", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Site": "same-site", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", "Uw-Path": "/flow/option_chains", "Uw-Sh": "8lgba0upqzOp4J3C" } # Generate strikes in descending order strikes = sorted((atm_strike + i * 5 for i in range(-5, 6)), reverse=True) expiry_str = expiry.strftime('%y%m%d') # Create columns for calls and puts side by side col_call, col_put = st.columns(2) for strike in strikes: modified_strike = f"0{strike}000" # Fetch and plot for call options call_url = f"https://phx.unusualwhales.com/api/chain_aggregates/{index}{expiry_str}C{modified_strike}/intraday?grouping_minutes=5&market_day_timeframe=1" call_data = fetch_data(call_url, headers) if call_data: df_call = pd.DataFrame(call_data) call_chart = create_chart(df_call, 'Call', strike) with col_call: st.altair_chart(call_chart, use_container_width=True) # Fetch and plot for put options put_url = f"https://phx.unusualwhales.com/api/chain_aggregates/{index}{expiry_str}P{modified_strike}/intraday?grouping_minutes=5&market_day_timeframe=1" put_data = fetch_data(put_url, headers) if put_data: df_put = pd.DataFrame(put_data) put_chart = create_chart(df_put, 'Put', strike) with col_put: st.altair_chart(put_chart, use_container_width=True)