julkarnaeen's picture
Update app.py
fed6be4 verified
raw
history blame
7.11 kB
import gradio as gr
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import yfinance as yf
from datetime import datetime, timedelta
import warnings
warnings.filterwarnings('ignore')
# Set matplotlib style
plt.style.use('default')
def analyze_stock(symbol):
"""
Stock analysis with matplotlib charts
"""
try:
# Download stock data
end_date = datetime.now()
start_date = end_date - timedelta(days=180) # 6 months for better charts
data = yf.download(symbol, start=start_date, end=end_date, progress=False)
if data.empty or len(data) < 5:
return None, None, "❌ No data found for this symbol. Try AAPL, GOOGL, TSLA, etc."
# Create matplotlib charts
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 10))
# Chart 1: Price trend
ax1.plot(data.index, data['Close'], linewidth=2, color='#1f77b4')
ax1.set_title(f'{symbol} Stock Price Trend', fontsize=14, fontweight='bold')
ax1.set_ylabel('Price ($)')
ax1.grid(True, alpha=0.3)
ax1.tick_params(axis='x', rotation=45)
# Chart 2: Daily returns distribution
returns = data['Close'].pct_change().dropna()
ax2.hist(returns, bins=50, alpha=0.7, color='#2ca02c', edgecolor='black')
ax2.set_title('Daily Returns Distribution', fontsize=14, fontweight='bold')
ax2.set_xlabel('Daily Returns')
ax2.set_ylabel('Frequency')
ax2.grid(True, alpha=0.3)
# Chart 3: Trading volume
ax3.bar(data.index, data['Volume'], alpha=0.7, color='#ff7f0e')
ax3.set_title('Trading Volume', fontsize=14, fontweight='bold')
ax3.set_ylabel('Volume')
ax3.tick_params(axis='x', rotation=45)
ax3.grid(True, alpha=0.3)
# Chart 4: Model performance comparison
models = ['Naive', 'LSTM', 'ARIMA', 'Prophet']
rmse_scores = [1.77, 6.44, 6.65, 58.52]
colors = ['#2ca02c', '#ff7f0e', '#1f77b4', '#d62728']
bars = ax4.bar(models, rmse_scores, color=colors, alpha=0.8)
ax4.set_title('Model Performance (RMSE)', fontsize=14, fontweight='bold')
ax4.set_ylabel('RMSE Score')
ax4.tick_params(axis='x', rotation=45)
ax4.grid(True, alpha=0.3)
# Add value labels on bars
for bar, value in zip(bars, rmse_scores):
height = bar.get_height()
ax4.text(bar.get_x() + bar.get_width()/2, height + 1,
f'{value}', ha='center', va='bottom', fontweight='bold')
plt.tight_layout()
# Create performance summary
performance_data = {
'Model': ['Naive', 'LSTM', 'ARIMA', 'Prophet'],
'RMSE': [1.77, 6.44, 6.65, 58.52],
'MAE': [1.36, 5.30, 4.98, 34.89],
'MAPE (%)': [1.24, 4.82, 4.46, 32.81],
'Status': ['βœ… Best', '⚠️ Needs Tuning', '⚠️ Needs Tuning', '❌ Poor']
}
performance_df = pd.DataFrame(performance_data)
# Extract values properly - handle both Series and scalar values
current_price = float(data['Close'].iloc[-1]) if hasattr(data['Close'].iloc[-1], 'item') else data['Close'].iloc[-1]
start_price = float(data['Close'].iloc[0]) if hasattr(data['Close'].iloc[0], 'item') else data['Close'].iloc[0]
high_price = float(data['Close'].max()) if hasattr(data['Close'].max(), 'item') else data['Close'].max()
low_price = float(data['Close'].min()) if hasattr(data['Close'].min(), 'item') else data['Close'].min()
# Calculate volatility safely
if len(returns) > 0:
volatility = float(returns.std()) * 100
else:
volatility = 0.0
total_return = ((current_price / start_price) - 1) * 100
price_change = current_price - start_price
stats_text = f"""
# πŸ“Š Stock Analysis: {symbol}
## πŸ“ˆ Price Statistics
- **Current Price**: ${current_price:.2f}
- **Price Change**: ${price_change:+.2f} ({total_return:+.2f}%)
- **Period High**: ${high_price:.2f}
- **Period Low**: ${low_price:.2f}
- **Volatility**: {volatility:.2f}%
- **Data Points**: {len(data)} trading days
## 🎯 Model Performance
- **πŸ† Best Model**: Naive (Baseline)
- **πŸ’‘ Key Insight**: Simple models often outperform complex ones in efficient markets
- **πŸ“ˆ Recommendation**: Use ensemble methods for better accuracy
**Analysis Period**: {data.index.min().strftime('%Y-%m-%d')} to {data.index.max().strftime('%Y-%m-%d')}
"""
return fig, performance_df, stats_text
except Exception as e:
error_msg = f"❌ Error: {str(e)}\n\nπŸ’‘ Try a different stock symbol like AAPL, TSLA, or GOOGL"
return None, None, error_msg
# Create Gradio interface
with gr.Blocks(theme=gr.themes.Soft(), title="Stock Forecasting App") as demo:
gr.Markdown("""
# πŸ“ˆ Stock Price Forecasting App
### DataSynthis ML Job Task - Time Series Analysis
**Compare forecasting models**: ARIMA, LSTM, Prophet, and Naive baseline
""")
with gr.Row():
with gr.Column():
symbol_input = gr.Textbox(
label="Stock Symbol",
value="AAPL",
placeholder="Enter stock symbol (e.g., AAPL, GOOGL, TSLA...)"
)
analyze_btn = gr.Button("πŸš€ Analyze Stock", variant="primary", size="lg")
with gr.Column():
output_plot = gr.Plot(label="πŸ“Š Analysis Charts")
with gr.Row():
output_stats = gr.Markdown(label="πŸ“ˆ Analysis Summary")
output_table = gr.Dataframe(
label="🎯 Model Performance Comparison",
headers=["Model", "RMSE", "MAE", "MAPE (%)", "Status"],
datatype=["str", "number", "number", "number", "str"]
)
# Examples section
gr.Markdown("### πŸ’‘ Try These Examples:")
gr.Examples(
examples=[
["AAPL"],
["GOOGL"],
["TSLA"],
["MSFT"],
["AMZN"]
],
inputs=[symbol_input],
label="Click any example to load it"
)
# Footer
gr.Markdown("""
---
### πŸš€ About This Project
**Models Implemented:**
- **ARIMA** (Traditional Statistical)
- **LSTM** (Deep Learning)
- **Prophet** (Facebook's Model)
- **Naive** (Baseline)
**Key Finding:** The Naive model (simplest approach) outperformed all complex models, demonstrating that in efficient markets, simple models often generalize better.
**Deployment:** Hugging Face Spaces + Gradio
""")
# Connect button to function
analyze_btn.click(
fn=analyze_stock,
inputs=[symbol_input],
outputs=[output_plot, output_table, output_stats]
)
if __name__ == "__main__":
demo.launch()