import yfinance as yf import pandas as pd import numpy as np import plotly.graph_objects as go from plotly.subplots import make_subplots import streamlit as st from datetime import datetime, timedelta # Helper function to fetch stock data def fetch_stock_data(ticker: str, start_date: str, end_date: str) -> pd.DataFrame: """Fetch stock 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 # Function to estimate probability and plot def estimate_probability(data, n_days, initial_price, up_target, down_target): # Calculate the thresholds for price change up_threshold = round(abs((up_target - initial_price) / initial_price), 3) down_threshold = round(abs((down_target - initial_price) / initial_price), 3) # Calculate the n-day percentage change data[f'{n_days}d_pct_change'] = data['Adj Close'].pct_change(n_days) # Calculate the frequency of the threshold price change total_periods = len(data.dropna(subset=[f'{n_days}d_pct_change'])) down_periods = len(data[data[f'{n_days}d_pct_change'] <= -down_threshold]) up_periods = len(data[data[f'{n_days}d_pct_change'] >= up_threshold]) down_frequency = down_periods / total_periods if total_periods > 0 else 0 up_frequency = up_periods / total_periods if total_periods > 0 else 0 # Plotting fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1, subplot_titles=(f'Distribution of {n_days}-day percentage changes', f'Price for {ticker}'), specs=[[{"secondary_y": False}], [{"secondary_y": False}]]) fig.add_trace(go.Histogram(x=data[f'{n_days}d_pct_change'], nbinsx=100, name='Percentage Change'), row=1, col=1) fig.add_vline(x=-down_threshold, line=dict(color='red', dash='dash'), annotation_text=f"{down_threshold * 100:.2f}% decrease frequency: {down_frequency * 100:.2f}%", annotation_position="bottom left", row=1, col=1) fig.add_vline(x=up_threshold, line=dict(color='blue', dash='dash'), annotation_text=f"{up_threshold * 100:.2f}% increase frequency: {up_frequency * 100:.2f}%", annotation_position="top right", row=1, col=1) fig.add_trace(go.Scatter(x=data.index, y=data['Adj Close'], mode='lines', name='Adjusted Close Price'), row=2, col=1) up_instances = data[data[f'{n_days}d_pct_change'] >= up_threshold] down_instances = data[data[f'{n_days}d_pct_change'] <= -down_threshold] fig.add_trace(go.Scatter(x=up_instances.index, y=up_instances['Adj Close'], mode='markers', marker=dict(color='blue', symbol='triangle-up', size=10), name=f"{up_threshold * 100:.2f}% increase"), row=2, col=1) fig.add_trace(go.Scatter(x=down_instances.index, y=down_instances['Adj Close'], mode='markers', marker=dict(color='red', symbol='triangle-down', size=10), name=f"{down_threshold * 100:.2f}% decrease"), row=2, col=1) fig.update_layout(title_text=f"Probability Estimation and Price for {ticker}", xaxis_title='Date', yaxis_title='Adjusted Close Price') return fig, up_frequency, down_frequency # Streamlit app st.set_page_config(page_title="Price Movements Probability Analysis", layout="wide") st.title('Price Probability Analysis') # Sidebar for method selection st.sidebar.title("Input Parameters") with st.sidebar.expander("How to Use", expanded=False): st.markdown(""" ### How to Use 1. **Enter Ticker**: Input the stock symbol or crypto pair you want to analyze. 2. **Select Date Range**: Choose the start and end dates for the historical data. 3. **Set Parameters**: - **Number of Days**: Define the period for percentage change calculation. - **Initial Price**: The starting price for your analysis. - **Up Target**: The target price for an upward move. - **Down Target**: The target price for a downward move. 4. **Run Analysis**: Click the 'Run Analysis' button to generate the results. """) with st.sidebar.expander("Ticker and Date Settings", expanded=True): ticker = st.text_input('Enter Ticker (Stock or Crypto Pair)', 'SAP.DE', help="Enter a stock ticker (e.g., AAPL) or a crypto pair (e.g., BTC-USD)") start_date = st.date_input('Start Date', pd.to_datetime('2020-01-01'), help="Select the start date for the data") end_date = st.date_input('End Date', datetime.now().date() + timedelta(days=1), help="Select the end date for the data") with st.sidebar.expander("Parameter Settings", expanded=True): # Fetch data to set default values only if Run Analysis is clicked n_days = st.slider('Number of Days', min_value=1, max_value=100, value=30, step=1, help="Number of days for percentage change calculation") initial_price = st.number_input('Initial Price', value=0.0, help="Starting price for analysis (defaults to latest price if 0)") up_target = st.number_input('Up Target', value=0.0, help="Target price for upward move (defaults to 10% above initial if 0)") down_target = st.number_input('Down Target', value=0.0, help="Target price for downward move (defaults to 10% below initial if 0)") # Main content area st.markdown(""" This app estimates the probability of a stock or cryptocurrency reaching certain price targets within a specified number of days based on historical data. - **Distribution of Percentage Changes**: The histogram shows the distribution of percentage changes over the selected number of days. - **Price Targets**: The vertical lines indicate the price targets for upward and downward moves. The frequencies of reaching these targets are annotated. - **Price Plot**: The line chart shows the historical adjusted close prices with markers indicating instances where the price targets were met. """) # Run button run_button = st.sidebar.button('Run Analysis') # Fetch data and display results if run_button: 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 data = st.session_state.data if initial_price <= 0: initial_price = data['Adj Close'].iloc[-1] if up_target <= 0: up_target = initial_price * 1.1 if down_target <= 0: down_target = initial_price * 0.9 fig, up_frequency, down_frequency = estimate_probability(data, n_days, initial_price, up_target, down_target) st.plotly_chart(fig) st.markdown(f""" ### Results **Probability of Reaching Targets:** - Probability of reaching the up target ({up_target:.2f}) in {n_days} days: **{up_frequency * 100:.2f}%** - Probability of reaching the down target ({down_target:.2f}) in {n_days} days: **{down_frequency * 100:.2f}%** This analysis helps in understanding the historical likelihood of the price reaching certain targets within a specified number of days. """) hide_streamlit_style = """ """ st.markdown(hide_streamlit_style, unsafe_allow_html=True)