Spaces:
Running
Running
import numpy as np | |
import yfinance as yf | |
import pandas as pd | |
import plotly.graph_objects as go | |
import streamlit as st | |
from plotly.subplots import make_subplots | |
from itertools import product | |
import warnings | |
from datetime import datetime | |
warnings.filterwarnings("ignore") | |
st.set_page_config(page_title="Expected Stock Price Movement Using Volatility Multipliers", layout="wide") | |
st.title('Expected Stock Price Movement Using Volatility Multipliers') | |
# Sidebar section with instructions | |
st.sidebar.title('Input Parameters') | |
with st.sidebar.expander("How to use:", expanded=False): | |
st.markdown(""" | |
1. **Input Parameters**: Enter the stock ticker or cryptocurrency pair, date range, time horizon, standard deviation multipliers, and rolling window period. | |
2. **Run the Analysis**: Click the "Run" button to perform the analyses and visualize the results. | |
""") | |
# Wrapping ticker and date settings in an expander | |
with st.sidebar.expander("Ticker and Date Settings", expanded=True): | |
ticker = st.text_input('Enter Stock Ticker or Crypto Pair', 'ADS.DE', help="Enter the ticker symbol of the stock or cryptocurrency pair you want to analyze (e.g., ADS.DE for Adidas, BTC-USD for Bitcoin).") | |
start_date = st.date_input('Start Date', pd.to_datetime('2020-01-01'), help="Select the start date for fetching historical stock data.") | |
end_date = st.date_input('End Date', pd.to_datetime('today') + pd.DateOffset(1), help="Select the end date for fetching historical stock data.") | |
# Wrapping parameter settings in an expander | |
with st.sidebar.expander("Parameter Settings", expanded=True): | |
time_horizon = st.slider('Time Horizon (Days)', min_value=1, max_value=60, value=30, help="Set the number of days into the future for which you want to estimate asset prices.") | |
std_multipliers = st.multiselect('Select Std Multipliers', [1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3], default=[1, 1.25, 1.5, 1.75], help="Choose the standard deviation multipliers to calculate future price ranges.") | |
rolling_window = st.slider('Rolling Window (Days)', min_value=10, max_value=90, value=30, step=5, help="Set the number of days to use for calculating rolling volatility.") | |
st.write(""" | |
This tool estimates the potential price movement of a selected stock or cryptocurrency pair over a specified time horizon. | |
The predictions are based on historical volatility, calculated from the asset's daily returns. | |
You can adjust the time horizon and standard deviation multipliers to see how the expected price range changes. | |
""") | |
with st.expander("Click here to read more about the methodology", expanded=False): | |
st.latex(r''' | |
P_t = P_0 \times e^{\sigma \times \sqrt{t} \times z} | |
''') | |
st.markdown(""" | |
**Formula for price movement estimation explained:** | |
- **Pt**: Estimated price at time (t) | |
- **P0**: Current price | |
- **σ (sigma)**: Standard deviation of the stock's returns, representing volatility | |
- **t**: Time horizon in days | |
- **z**: Multiplier corresponding to the desired confidence level, which adjusts for standard deviation | |
To read more about the methodologies, visit [this link](https://entreprenerdly.com/expected-stock-price-movement-with-volatility-multipliers/). | |
""") | |
if st.sidebar.button('Run Analysis'): | |
# Download data; use "Close" and squeeze to ensure a 1D Series. | |
stock_data = yf.download(ticker, start=start_date, end=end_date) | |
if not stock_data.empty: | |
stock_data = stock_data['Close'].squeeze() | |
# Compute returns separately (do not add to stock_data to avoid modifying the index) | |
returns = stock_data.pct_change() | |
current_price = stock_data.iloc[-1] | |
# Method 1: Volatility over dynamic periods | |
fig1 = go.Figure() | |
plot_data = stock_data[-rolling_window:] | |
# Ensure the last index is a Timestamp (avoid concatenation error) | |
last_date = pd.to_datetime(plot_data.index[-1]) | |
date_range = pd.date_range(last_date + pd.DateOffset(1), periods=time_horizon, freq='D') | |
st.markdown(""" | |
### Method 1: Dynamic Volatility | |
This method assesses stock price movement by calculating volatility over dynamically changing periods based on a rolling window. | |
""") | |
# Plot historical prices once | |
fig1.add_trace(go.Scatter(x=plot_data.index, y=plot_data, mode='lines', name='Historical Prices')) | |
for std_multiplier in std_multipliers: | |
expected_upper_bounds = pd.Series(index=date_range) | |
expected_lower_bounds = pd.Series(index=date_range) | |
for i in range(time_horizon): | |
if i == 0: | |
volatility = stock_data.iloc[-rolling_window:].pct_change().std() | |
else: | |
volatility = stock_data.iloc[-(rolling_window + i):-i].pct_change().std() | |
expected_price_movement = current_price * volatility * np.sqrt(i + 1) * std_multiplier | |
expected_upper_bounds.iloc[i] = current_price + expected_price_movement | |
expected_lower_bounds.iloc[i] = current_price - expected_price_movement | |
fig1.add_trace(go.Scatter(x=date_range, y=expected_upper_bounds, mode='lines', line=dict(dash='dash', color='green'), name=f'Upper Bound ({std_multiplier}x std)')) | |
fig1.add_trace(go.Scatter(x=date_range, y=expected_lower_bounds, mode='lines', line=dict(dash='dash', color='red'), name=f'Lower Bound ({std_multiplier}x std)')) | |
fig1.add_trace(go.Scatter(x=date_range, y=expected_upper_bounds, fill='tonexty', fillcolor='rgba(128, 128, 128, 0.3)', mode='none', showlegend=False)) | |
fig1.update_layout(title=f'{ticker} - Dynamic Volatility Expected Price Movement', | |
xaxis_title='Date', | |
yaxis_title='Price', | |
legend_title='Legend', | |
width=1600, | |
height=800) | |
# Method 2: Single volatility measure over the period | |
fig2 = go.Figure() | |
# Plot historical prices once | |
fig2.add_trace(go.Scatter(x=plot_data.index, y=plot_data, mode='lines', name='Historical Prices')) | |
for std_multiplier in std_multipliers: | |
expected_upper_bounds = pd.Series(index=date_range) | |
expected_lower_bounds = pd.Series(index=date_range) | |
for i in range(time_horizon): | |
volatility = stock_data.pct_change().std() * std_multiplier | |
expected_price_movement = current_price * volatility * np.sqrt(i + 1) | |
expected_upper_bounds.iloc[i] = current_price + expected_price_movement | |
expected_lower_bounds.iloc[i] = current_price - expected_price_movement | |
fig2.add_trace(go.Scatter(x=date_range, y=expected_upper_bounds, mode='lines', line=dict(dash='dash', color='green'), name=f'Upper Bound ({std_multiplier}x std)')) | |
fig2.add_trace(go.Scatter(x=date_range, y=expected_lower_bounds, mode='lines', line=dict(dash='dash', color='red'), name=f'Lower Bound ({std_multiplier}x std)')) | |
fig2.add_trace(go.Scatter(x=date_range, y=expected_upper_bounds, fill='tonexty', fillcolor='rgba(128, 128, 128, 0.3)', mode='none', showlegend=False)) | |
fig2.update_layout(title=f'{ticker} - Single Volatility Measure Expected Price Movement', | |
xaxis_title='Date', | |
yaxis_title='Price', | |
legend_title='Legend', | |
width=1600, | |
height=800) | |
st.plotly_chart(fig1) | |
st.markdown(""" | |
### Method 2: Single Volatility Measure | |
This method calculates stock price movement based on a single, constant measure of volatility derived from the entire historical data set available. | |
""") | |
st.plotly_chart(fig2) | |
else: | |
st.write("No data found for the given ticker and date range.") | |
# Hide Streamlit's menu and footer | |
hide_streamlit_style = """ | |
<style> | |
#MainMenu {visibility: hidden;} | |
footer {visibility: hidden;} | |
</style> | |
""" | |
st.markdown(hide_streamlit_style, unsafe_allow_html=True) | |