Spaces:
Sleeping
Sleeping
File size: 10,527 Bytes
eec67e7 78bf65f eec67e7 78bf65f f35cc67 a5fac15 eec67e7 a5fac15 eec67e7 380a39d eec67e7 6856892 6125812 faf3f39 eec67e7 78bf65f 53ff493 78bf65f 15fbd06 a5fac15 15fbd06 78bf65f 53ff493 78bf65f eec67e7 a5fac15 eec67e7 a5fac15 980572e a5fac15 30a97ff 78bf65f 30a97ff a5fac15 | 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 | import pandas as pd
import numpy as np
import yfinance as yf
import streamlit as st
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# Helper function to fetch stock or crypto data
def fetch_stock_data(ticker: str, start_date: str, end_date: str) -> pd.DataFrame:
"""Fetch stock or crypto data from Yahoo Finance."""
data = yf.download(ticker, start=start_date, end=end_date, auto_adjust=False)
if isinstance(data.columns, pd.MultiIndex):
data.columns = data.columns.get_level_values(0)
return data
# True Range Volatility Ratio
def calculate_volatility_ratio(data: pd.DataFrame, n: int = 14, ratio: float = 1.75) -> go.Figure:
data['High_Low'] = data['High'] - data['Low']
data['High_PrevClose'] = abs(data['High'] - data['Close'].shift())
data['Low_PrevClose'] = abs(data['Low'] - data['Close'].shift())
data['True_Range'] = data[['High_Low', 'High_PrevClose', 'Low_PrevClose']].max(axis=1)
data['EMA_True_Range'] = data['True_Range'].ewm(span=n, adjust=False).mean()
data['Volatility_Ratio'] = data['True_Range'] / data['EMA_True_Range']
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
subplot_titles=('Close Price', 'Volatility Ratio'))
fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price'), row=1, col=1)
above_threshold = data['Volatility_Ratio'] > ratio
fig.add_trace(go.Scatter(x=data.index[above_threshold], y=data['Close'][above_threshold], mode='markers',
marker=dict(color='red', size=10), name=f'Volatility Ratio > {ratio}'), row=1, col=1)
fig.add_trace(go.Scatter(x=data.index, y=data['Volatility_Ratio'], mode='lines', name='Volatility Ratio'), row=2, col=1)
fig.add_hline(y=ratio, line=dict(color='red', dash='dash'), row=2, col=1)
fig.update_layout(title='Volatility Ratio with Buy/Sell Signals', xaxis_title='Date', yaxis_title='Price', yaxis2_title='Volatility Ratio')
return fig
# Volume Ratio with Dynamic Percentile
def calculate_volume_ratio(data: pd.DataFrame, percentile_threshold: float = 0.97) -> go.Figure:
data['50_day_MA'] = data['Close'].rolling(window=50).mean()
data['200_day_MA'] = data['Close'].rolling(window=200).mean()
data['Volume_MA20'] = data['Volume'].rolling(window=20).mean()
# Ensure Volume_Ratio is a single-column Series with proper index alignment
volume_ratio = (data['Volume'] / data['Volume_MA20']).reindex(data.index, method='ffill')
data['Volume_Ratio'] = pd.Series(volume_ratio, index=data.index)
dynamic_percentile = data['Volume_Ratio'].quantile(percentile_threshold)
mask = data['Volume_Ratio'] > dynamic_percentile
fig = make_subplots(rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.1,
subplot_titles=('Close Price and Volume', 'Volume Ratio', 'Histogram of Volume Ratio'),
specs=[[{"secondary_y": True}], [{}], [{}]])
# Plotting close price on primary y-axis
fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price', line=dict(color='blue')), row=1, col=1, secondary_y=False)
fig.add_trace(go.Scatter(x=data.index[mask], y=data['Close'][mask], mode='markers',
marker=dict(color='red', size=10), name='High Volume Ratio'), row=1, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=data.index, y=data['Volume'], name='Volume', marker_color='gray', opacity=0.3), row=1, col=1, secondary_y=True)
fig.add_trace(go.Scatter(x=data.index, y=data['Volume_MA20'], mode='lines', name='20-day Volume MA', line=dict(color='purple')), row=1, col=1, secondary_y=True)
fig.add_trace(go.Scatter(x=data.index, y=data['Volume_Ratio'], mode='lines', name='Volume Ratio', line=dict(color='green')), row=2, col=1)
fig.add_hline(y=dynamic_percentile, line=dict(color='red', dash='dash'), row=2, col=1)
fig.add_trace(go.Histogram(x=data['Volume_Ratio'], nbinsx=50, name='Volume Ratio Distribution', marker_color='green', opacity=0.7), row=3, col=1)
fig.add_vline(x=dynamic_percentile, line=dict(color='red', dash='dash'), row=3, col=1)
fig.update_layout(title='Volume Ratio with Dynamic Percentile Threshold', xaxis_title='Date', yaxis1_title='Price', yaxis2_title='Volume', yaxis3_title='Volume Ratio', yaxis4_title='Frequency')
return fig
# Streamlit app
st.set_page_config(page_title="Unusual Volatility and Volume", layout="wide")
st.title('Unusual Volatility and Volume')
st.sidebar.title('Input Parameters')
with st.sidebar.expander("Select Analysis", expanded=True):
selected = st.radio("Select Analysis", ["Volatility Ratio", "Volume Ratio"])
# Sidebar for "How to Use" instructions specific to the selected method
with st.sidebar.expander("How to Use", expanded=False):
if selected == "Volatility Ratio":
st.markdown("""
**How to Use Volatility Ratio:**
1. Enter the stock or crypto symbol (e.g., 'AAPL' for Apple or 'BTC-USD' for Bitcoin).
2. Choose the date range.
3. Set the number of days for the EMA calculation.
4. Set the volatility ratio threshold.
5. Click 'Fetch Data' to load the data.
6. The chart will display the True Range Volatility Ratio and highlight points where the ratio exceeds the threshold.
""")
elif selected == "Volume Ratio":
st.markdown("""
**How to Use Volume Ratio:**
1. Enter the stock or crypto symbol.
2. Choose the date range.
3. Set the percentile threshold.
4. Click 'Fetch Data' to load the data.
5. The chart will display the Volume Ratio and highlight points where the ratio exceeds the percentile threshold.
""")
# Input parameters inside an expander
with st.sidebar.expander("Input Parameters", expanded=True):
ticker = st.text_input('Enter Stock or Crypto Symbol (e.g., AAPL or BTC-USD)', 'AAPL', help="Enter the ticker symbol for the stock or cryptocurrency you want to analyze.")
start_date = st.date_input('Start Date', pd.to_datetime('2020-01-01'), help="Select the start date for the data range.")
end_date = st.date_input('End Date', pd.to_datetime(pd.Timestamp.now().date() + pd.Timedelta(days=1)), help="Select the end date for the data range.")
fetch_data = st.button('Fetch Data') # Capture the button state
# Fetch data
if 'data' not in st.session_state or fetch_data:
data = fetch_stock_data(ticker, start_date, end_date)
if data.empty:
st.error(f"No data returned for {ticker} from {start_date} to {end_date}")
else:
st.session_state.data = data
# Parameters for each method inside an expander
with st.sidebar.expander(f"Parameters for {selected}", expanded=True):
if selected == "Volatility Ratio":
n = st.number_input('Number of Days for EMA', min_value=1, value=14, help="Set the number of days for the Exponential Moving Average (EMA) calculation.")
ratio = st.slider('Volatility Ratio Threshold', min_value=1.0, max_value=3.0, value=1.75, step=0.1, help="Set the threshold for the Volatility Ratio to identify high volatility periods.")
elif selected == "Volume Ratio":
percentile_threshold = st.slider('Percentile Threshold', min_value=0.5, max_value=1.0, value=0.97, step=0.01, help="Set the percentile threshold for identifying high volume periods.")
if 'data' in st.session_state and not st.session_state.data.empty:
data = st.session_state.data
# Display results based on the selected method
if selected == "Volatility Ratio":
st.markdown("### Volatility Ratio")
st.markdown("This method calculates the True Range Volatility Ratio, which helps identify periods of high volatility. A ratio above a specified threshold indicates high volatility.")
with st.expander("How it Works", expanded=False):
st.markdown("""
**How Volatility Ratio Works:**
1. **Calculate True Range:**
- True Range is the maximum of the following:
""")
st.latex(r'''
\text{True Range} = \max(\text{High} - \text{Low}, \left| \text{High} - \text{Previous Close} \right|, \left| \text{Low} - \text{Previous Close} \right|)
''')
st.markdown("""
2. **Compute Exponential Moving Average (EMA) of True Range:**
- Calculate the EMA of the True Range over a specified period (e.g., 14 days).
3. **Calculate Volatility Ratio:**
- Use the formula:
""")
st.latex(r'''
\text{Volatility Ratio} = \frac{\text{True Range}}{\text{EMA of True Range}}
''')
st.markdown("""
4. **Identify Signals:**
- **High Volatility:** Occurs when the Volatility Ratio exceeds a specified threshold (e.g., 1.75).
""")
fig = calculate_volatility_ratio(data, n, ratio)
st.plotly_chart(fig)
elif selected == "Volume Ratio":
st.markdown("### Volume Ratio with Dynamic Percentile")
st.markdown("This method calculates the Volume Ratio and uses a dynamic percentile threshold to identify high volume periods. A volume ratio above the specified percentile indicates high trading activity.")
with st.expander("How it Works", expanded=False):
st.markdown("""
**How Volume Ratio Works:**
1. **Calculate Volume Moving Average:**
- Compute the 20-day moving average of the volume.
2. **Calculate Volume Ratio:**
- Use the formula:
""")
st.latex(r'''
\text{Volume Ratio} = \frac{\text{Volume}}{\text{20-day Moving Average of Volume}}
''')
st.markdown("""
3. **Determine Dynamic Percentile Threshold:**
- Calculate the specified percentile of the Volume Ratio distribution.
4. **Identify Signals:**
- **High Volume:** Occurs when the Volume Ratio exceeds the dynamic percentile threshold.
""")
fig = calculate_volume_ratio(data, percentile_threshold)
st.plotly_chart(fig)
# Hide the default Streamlit menu and footer
hide_streamlit_style = """
<style>
#MainMenu {visibility: hidden;}
footer {visibility: hidden;}
</style>
"""
st.markdown(hide_streamlit_style, unsafe_allow_html=True) |