Spaces:
Running
Running
import gradio as gr | |
import yfinance as yf | |
import pandas as pd | |
import numpy as np | |
import plotly.graph_objects as go | |
import warnings | |
import time | |
import gc | |
import os | |
import torch | |
from datetime import datetime, timedelta | |
from typing import Optional, Dict, Any, Tuple | |
warnings.filterwarnings('ignore') | |
# Environment optimizations for Hugging Face Spaces | |
os.environ['TOKENIZERS_PARALLELISM'] = 'false' | |
os.environ['HF_HUB_DISABLE_PROGRESS_BARS'] = '1' | |
os.environ['HF_HUB_DISABLE_TELEMETRY'] = '1' | |
os.environ['TRANSFORMERS_VERBOSITY'] = 'error' | |
if torch.cuda.is_available(): | |
torch.cuda.empty_cache() | |
torch.set_num_threads(min(4, os.cpu_count() or 1)) | |
class FastAIStockAnalyzer: | |
"""Optimized AI Stock Analyzer for Gradio with robust error handling""" | |
def __init__(self): | |
self.context_length = 32 | |
self.prediction_length = 7 | |
self.device = "cpu" | |
self.model_cache = {} | |
def fetch_stock_data(self, symbol: str, period: str = "6mo") -> Tuple[Optional[pd.DataFrame], Optional[Dict]]: | |
"""Fetch stock data with error handling""" | |
try: | |
ticker = yf.Ticker(symbol) | |
data = ticker.history(period=period, interval="1d", | |
actions=False, auto_adjust=True, | |
back_adjust=False, repair=False) | |
if data.empty: | |
return None, None | |
try: | |
info = { | |
'longName': ticker.info.get('longName', symbol), | |
'sector': ticker.info.get('sector', 'Unknown'), | |
'marketCap': ticker.info.get('marketCap', 0) | |
} | |
except: | |
info = {'longName': symbol, 'sector': 'Unknown', 'marketCap': 0} | |
return data, info | |
except Exception as e: | |
return None, None | |
def load_chronos_tiny(self) -> Tuple[Optional[Any], str]: | |
"""Load Chronos model with caching and fallback""" | |
model_key = "chronos_tiny" | |
if model_key in self.model_cache: | |
return self.model_cache[model_key], "chronos" | |
try: | |
from chronos import ChronosPipeline | |
# Try primary loading method | |
pipeline = ChronosPipeline.from_pretrained( | |
"amazon/chronos-t5-tiny", | |
device_map="cpu", | |
torch_dtype=torch.float32, | |
low_cpu_mem_usage=True, | |
trust_remote_code=True | |
) | |
self.model_cache[model_key] = pipeline | |
return pipeline, "chronos" | |
except ImportError: | |
# Chronos not available | |
return None, None | |
except Exception as e: | |
# Try fallback loading method | |
try: | |
pipeline = ChronosPipeline.from_pretrained( | |
"amazon/chronos-t5-tiny", | |
device_map="auto", | |
torch_dtype=torch.float32 | |
) | |
self.model_cache[model_key] = pipeline | |
return pipeline, "chronos" | |
except: | |
return None, None | |
def load_moirai_small(self) -> Tuple[Optional[Any], str]: | |
"""Load Moirai model with updated method and fallbacks""" | |
model_key = "moirai_small" | |
if model_key in self.model_cache: | |
return self.model_cache[model_key], "moirai" | |
try: | |
from uni2ts.model.moirai import MoiraiForecast, MoiraiModule | |
# Method 1: Try the standard approach | |
try: | |
module = MoiraiModule.from_pretrained( | |
"Salesforce/moirai-1.0-R-small", | |
device_map="cpu", | |
torch_dtype=torch.float32, | |
trust_remote_code=True, | |
low_cpu_mem_usage=True | |
) | |
model = MoiraiForecast( | |
module=module, | |
prediction_length=self.prediction_length, | |
context_length=self.context_length, | |
patch_size="auto", | |
num_samples=10, | |
target_dim=1, | |
feat_dynamic_real_dim=0, | |
past_feat_dynamic_real_dim=0 | |
) | |
self.model_cache[model_key] = model | |
return model, "moirai" | |
except Exception as e1: | |
# Method 2: Try newer version | |
try: | |
module = MoiraiModule.from_pretrained( | |
"Salesforce/moirai-1.1-R-small", | |
device_map="cpu", | |
torch_dtype=torch.float32, | |
trust_remote_code=True | |
) | |
model = MoiraiForecast( | |
module=module, | |
prediction_length=self.prediction_length, | |
context_length=self.context_length, | |
patch_size="auto", | |
num_samples=5, # Reduced for stability | |
target_dim=1, | |
feat_dynamic_real_dim=0, | |
past_feat_dynamic_real_dim=0 | |
) | |
self.model_cache[model_key] = model | |
return model, "moirai" | |
except Exception as e2: | |
# Method 3: Minimal configuration | |
try: | |
module = MoiraiModule.from_pretrained("Salesforce/moirai-1.0-R-small") | |
model = MoiraiForecast( | |
module=module, | |
prediction_length=7, | |
context_length=32, | |
patch_size="auto", | |
num_samples=5, | |
target_dim=1 | |
) | |
self.model_cache[model_key] = model | |
return model, "moirai" | |
except Exception as e3: | |
return None, None | |
except ImportError: | |
# uni2ts not available | |
return None, None | |
except Exception as e: | |
return None, None | |
def predict_chronos_fast(self, pipeline: Any, data: np.ndarray) -> Optional[Dict]: | |
"""Fast Chronos prediction with error handling""" | |
try: | |
context_data = data[-self.context_length:] | |
context = torch.tensor(context_data, dtype=torch.float32).unsqueeze(0) | |
with torch.no_grad(): | |
forecast = pipeline.predict( | |
context=context, | |
prediction_length=self.prediction_length, | |
num_samples=10, | |
temperature=1.0, | |
top_k=50, | |
top_p=1.0 | |
) | |
forecast_array = forecast[0].numpy() | |
predictions = { | |
'mean': np.median(forecast_array, axis=0), | |
'q10': np.quantile(forecast_array, 0.1, axis=0), | |
'q90': np.quantile(forecast_array, 0.9, axis=0), | |
'std': np.std(forecast_array, axis=0) | |
} | |
return predictions | |
except Exception as e: | |
return None | |
def predict_moirai_fast(self, model: Any, data: np.ndarray) -> Optional[Dict]: | |
"""Fast Moirai prediction with enhanced error handling""" | |
try: | |
from gluonts.dataset.common import ListDataset | |
# Prepare dataset with minimal configuration | |
dataset = ListDataset([{ | |
"item_id": "stock", | |
"start": "2023-01-01", | |
"target": data[-self.context_length:].tolist() | |
}], freq='D') | |
# Create predictor with safe parameters | |
predictor = model.create_predictor( | |
batch_size=1, | |
num_parallel_samples=5 # Further reduced for stability | |
) | |
# Generate forecast | |
forecasts = list(predictor.predict(dataset)) | |
if not forecasts: | |
return None | |
forecast = forecasts[0] | |
predictions = { | |
'mean': forecast.mean, | |
'q10': forecast.quantile(0.1), | |
'q90': forecast.quantile(0.9), | |
'std': np.std(forecast.samples, axis=0) if hasattr(forecast, 'samples') else np.zeros(7) | |
} | |
return predictions | |
except Exception as e: | |
return None | |
def generate_simple_prediction(self, data: np.ndarray) -> Dict: | |
"""Fallback prediction method using simple statistical models""" | |
try: | |
# Simple moving average with trend | |
recent_data = data[-30:] # Last 30 days | |
short_ma = np.mean(recent_data[-7:]) # 7-day average | |
long_ma = np.mean(recent_data[-21:]) # 21-day average | |
# Calculate trend | |
trend = (short_ma - long_ma) / long_ma if long_ma != 0 else 0 | |
# Generate predictions | |
current_price = data[-1] | |
predictions = [] | |
for i in range(7): | |
# Simple trend projection with some noise | |
predicted_price = current_price * (1 + trend * (i + 1) * 0.1) | |
predictions.append(predicted_price) | |
predictions = np.array(predictions) | |
return { | |
'mean': predictions, | |
'q10': predictions * 0.95, # 5% lower | |
'q90': predictions * 1.05, # 5% higher | |
'std': np.full(7, np.std(recent_data) * 0.5) | |
} | |
except Exception: | |
# Ultimate fallback - flat prediction | |
current_price = data[-1] | |
return { | |
'mean': np.full(7, current_price), | |
'q10': np.full(7, current_price * 0.98), | |
'q90': np.full(7, current_price * 1.02), | |
'std': np.full(7, 0.01) | |
} | |
# Initialize analyzer globally for caching | |
analyzer = FastAIStockAnalyzer() | |
def analyze_stock(stock_symbol, model_choice, investment_amount, progress=gr.Progress()): | |
"""Main analysis function with comprehensive error handling and fallbacks""" | |
try: | |
progress(0.1, desc="Fetching stock data...") | |
# Validate input | |
if not stock_symbol or stock_symbol.strip() == "": | |
return ( | |
"β Error: Please enter a valid stock symbol.", | |
None, | |
"β Invalid Input", | |
"N/A", | |
"N/A" | |
) | |
# Fetch data | |
stock_data, stock_info = analyzer.fetch_stock_data(stock_symbol.upper()) | |
if stock_data is None or len(stock_data) < 50: | |
return ( | |
f"β Error: Insufficient data for {stock_symbol.upper()}. Please check the stock symbol or try a different one.", | |
None, | |
"β Data Error", | |
"N/A", | |
"N/A" | |
) | |
current_price = stock_data['Close'].iloc[-1] | |
company_name = stock_info.get('longName', stock_symbol) if stock_info else stock_symbol | |
progress(0.3, desc="Loading AI model...") | |
# Determine model type and load | |
model_type = "chronos" if "Chronos" in model_choice else "moirai" | |
model = None | |
model_name = "" | |
prediction_method = None | |
if model_type == "chronos": | |
model, loaded_type = analyzer.load_chronos_tiny() | |
model_name = "Amazon Chronos Tiny" | |
prediction_method = "chronos" | |
else: | |
model, loaded_type = analyzer.load_moirai_small() | |
model_name = "Salesforce Moirai Small" | |
prediction_method = "moirai" | |
# Fallback to Chronos if Moirai fails | |
if model is None: | |
progress(0.4, desc="Moirai unavailable, switching to Chronos...") | |
model, loaded_type = analyzer.load_chronos_tiny() | |
model_name = "Amazon Chronos Tiny (Fallback)" | |
prediction_method = "chronos" | |
# If both models fail, use simple prediction | |
if model is None: | |
progress(0.5, desc="Using statistical fallback method...") | |
model_name = "Statistical Trend Model (Fallback)" | |
prediction_method = "simple" | |
progress(0.6, desc="Generating predictions...") | |
# Generate predictions based on available method | |
predictions = None | |
if prediction_method == "chronos" and model is not None: | |
predictions = analyzer.predict_chronos_fast(model, stock_data['Close'].values) | |
elif prediction_method == "moirai" and model is not None: | |
predictions = analyzer.predict_moirai_fast(model, stock_data['Close'].values) | |
# Use simple prediction if AI models fail | |
if predictions is None: | |
predictions = analyzer.generate_simple_prediction(stock_data['Close'].values) | |
model_name = "Statistical Trend Model (AI Models Unavailable)" | |
progress(0.8, desc="Calculating investment scenarios...") | |
# Analysis results | |
mean_pred = predictions['mean'] | |
final_pred = mean_pred[-1] | |
week_change = ((final_pred - current_price) / current_price) * 100 | |
# Decision logic | |
if week_change > 5: | |
decision = "π’ STRONG BUY" | |
explanation = "Model expects significant gains!" | |
elif week_change > 2: | |
decision = "π’ BUY" | |
explanation = "Model expects moderate gains" | |
elif week_change < -5: | |
decision = "π΄ STRONG SELL" | |
explanation = "Model expects significant losses" | |
elif week_change < -2: | |
decision = "π΄ SELL" | |
explanation = "Model expects losses" | |
else: | |
decision = "βͺ HOLD" | |
explanation = "Model expects stable prices" | |
# Create analysis text | |
analysis_text = f""" | |
# π― {company_name} ({stock_symbol.upper()}) Analysis | |
## π€ RECOMMENDATION: {decision} | |
**{explanation}** | |
*Powered by {model_name}* | |
## π Key Metrics | |
- **Current Price**: ${current_price:.2f} | |
- **7-Day Prediction**: ${final_pred:.2f} ({week_change:+.2f}%) | |
- **Confidence Level**: {min(100, max(50, 70 + abs(week_change) * 1.5)):.0f}% | |
- **Analysis Method**: {model_name} | |
## π° Investment Scenario (${investment_amount:,.0f}) | |
- **Shares**: {investment_amount/current_price:.2f} | |
- **Current Value**: ${investment_amount:,.2f} | |
- **Predicted Value**: ${investment_amount + ((final_pred - current_price) * (investment_amount/current_price)):,.2f} | |
- **Profit/Loss**: ${((final_pred - current_price) * (investment_amount/current_price)):+,.2f} ({week_change:+.2f}%) | |
## β οΈ Important Disclaimers | |
- **This is for educational purposes only** | |
- **Not financial advice - consult professionals** | |
- **AI predictions can be wrong - invest responsibly** | |
- **Past performance β future results** | |
""" | |
progress(0.9, desc="Creating visualizations...") | |
# Create chart | |
fig = go.Figure() | |
# Historical data (last 30 days) | |
recent = stock_data.tail(30) | |
fig.add_trace(go.Scatter( | |
x=recent.index, | |
y=recent['Close'], | |
mode='lines', | |
name='Historical Price', | |
line=dict(color='blue', width=2) | |
)) | |
# Predictions | |
future_dates = pd.date_range( | |
start=stock_data.index[-1] + pd.Timedelta(days=1), | |
periods=7, | |
freq='D' | |
) | |
fig.add_trace(go.Scatter( | |
x=future_dates, | |
y=mean_pred, | |
mode='lines+markers', | |
name='Prediction', | |
line=dict(color='red', width=3), | |
marker=dict(size=8) | |
)) | |
# Confidence bands | |
if 'q10' in predictions and 'q90' in predictions: | |
fig.add_trace(go.Scatter( | |
x=future_dates.tolist() + future_dates[::-1].tolist(), | |
y=predictions['q90'].tolist() + predictions['q10'][::-1].tolist(), | |
fill='toself', | |
fillcolor='rgba(255,0,0,0.1)', | |
line=dict(color='rgba(255,255,255,0)'), | |
name='Confidence Range', | |
showlegend=True | |
)) | |
fig.update_layout( | |
title=f"{stock_symbol.upper()} - Stock Forecast ({model_name})", | |
xaxis_title="Date", | |
yaxis_title="Price ($)", | |
height=500, | |
showlegend=True, | |
template="plotly_white" | |
) | |
progress(1.0, desc="Analysis complete!") | |
# Create summary metrics | |
try: | |
day_change = stock_data['Close'].iloc[-1] - stock_data['Close'].iloc[-2] | |
day_change_pct = (day_change / stock_data['Close'].iloc[-2]) * 100 | |
except: | |
day_change_pct = 0 | |
current_metrics = f"${current_price:.2f} ({day_change_pct:+.2f}%)" | |
prediction_metrics = f"${final_pred:.2f} ({week_change:+.2f}%)" | |
return ( | |
analysis_text, | |
fig, | |
decision, | |
current_metrics, | |
prediction_metrics | |
) | |
except Exception as e: | |
# Ultimate error handler | |
error_msg = f""" | |
# β Analysis Error | |
**Something went wrong during the analysis:** | |
- **Error**: {str(e)[:200]}... | |
- **Stock**: {stock_symbol} | |
- **Model**: {model_choice} | |
## π§ Try these solutions: | |
1. **Check stock symbol** - Make sure it's valid (e.g., AAPL, GOOGL) | |
2. **Try different model** - Switch between Chronos and Moirai | |
3. **Refresh and try again** - Temporary server issues | |
4. **Use popular stocks** - AAPL, MSFT, GOOGL work best | |
## π Still having issues? | |
This may be due to Hugging Face Spaces resource limitations or model availability. | |
""" | |
return ( | |
error_msg, | |
None, | |
"β Error", | |
"N/A", | |
"N/A" | |
) | |
# Create Gradio interface with enhanced UI and improved disclaimer | |
with gr.Blocks( | |
theme=gr.themes.Soft(), | |
title="β‘ Fast AI Stock Predictor", | |
css=""" | |
footer {visibility: hidden} | |
.gradio-container {max-width: 1200px; margin: auto;} | |
.main-header {text-align: center; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px; margin-bottom: 20px;} | |
.disclaimer {background-color: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; border-radius: 8px; margin: 10px 0;} | |
""" | |
) as demo: | |
gr.HTML(""" | |
<div class="main-header"> | |
<h1 style="margin: 0; font-size: 2.5em;">β‘ AI Stock Predictor</h1> | |
<p style="margin: 10px 0 0 0; font-size: 1.2em;"><strong>π€ Powered by Amazon Chronos & Salesforce Moirai</strong></p> | |
<p style="margin: 5px 0 0 0; opacity: 0.9;">Advanced AI models for stock price forecasting</p> | |
</div> | |
""") | |
# Enhanced Disclaimer Section with Fully Visible Headings | |
gr.HTML(""" | |
<div style="max-width: 900px; margin: 20px auto; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;"> | |
<!-- Trading Decision Guide for Beginners --> | |
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 25px; margin: 30px 0;"> | |
<!-- BUY Signal --> | |
<div style="background: #27ae60; | |
color: #ffffff; | |
padding: 25px; | |
border-radius: 15px; | |
text-align: center; | |
box-shadow: 0 8px 25px rgba(46, 204, 113, 0.4); | |
border: 4px solid #229954;"> | |
<div style="font-size: 3em; margin-bottom: 15px;">π</div> | |
<h3 style="margin: 0 0 15px 0; | |
font-size: 1.3em; | |
font-weight: bold; | |
color: #ffffff; | |
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);"> | |
GREEN = BUY SIGNAL | |
</h3> | |
<p style="margin: 0; | |
font-size: 14px; | |
color: #ffffff; | |
line-height: 1.4;"> | |
AI predicts price increase<br> | |
<strong style="color: #ffffff;">β οΈ Still risky - do research!</strong> | |
</p> | |
</div> | |
<!-- HOLD Signal --> | |
<div style="background: #f39c12; | |
color: #ffffff; | |
padding: 25px; | |
border-radius: 15px; | |
text-align: center; | |
box-shadow: 0 8px 25px rgba(243, 156, 18, 0.4); | |
border: 4px solid #e67e22;"> | |
<div style="font-size: 3em; margin-bottom: 15px;">βͺ</div> | |
<h3 style="margin: 0 0 15px 0; | |
font-size: 1.3em; | |
font-weight: bold; | |
color: #ffffff; | |
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);"> | |
YELLOW = HOLD | |
</h3> | |
<p style="margin: 0; | |
font-size: 14px; | |
color: #ffffff; | |
line-height: 1.4;"> | |
AI expects stable prices<br> | |
<strong style="color: #ffffff;">Wait and watch strategy</strong> | |
</p> | |
</div> | |
<!-- SELL Signal --> | |
<div style="background: #e74c3c; | |
color: #ffffff; | |
padding: 25px; | |
border-radius: 15px; | |
text-align: center; | |
box-shadow: 0 8px 25px rgba(231, 76, 60, 0.4); | |
border: 4px solid #c0392b;"> | |
<div style="font-size: 3em; margin-bottom: 15px;">π</div> | |
<h3 style="margin: 0 0 15px 0; | |
font-size: 1.3em; | |
font-weight: bold; | |
color: #ffffff; | |
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);"> | |
RED = SELL SIGNAL | |
</h3> | |
<p style="margin: 0; | |
font-size: 14px; | |
color: #ffffff; | |
line-height: 1.4;"> | |
AI predicts price decrease<br> | |
<strong style="color: #ffffff;">Consider reducing exposure</strong> | |
</p> | |
</div> | |
</div> | |
<!-- Final Warning --> | |
<div style="background: #2c3e50; | |
color: #ffffff; | |
padding: 25px; | |
border-radius: 12px; | |
text-align: center; | |
margin-top: 30px; | |
border: 4px solid #34495e; | |
box-shadow: 0 8px 25px rgba(44, 62, 80, 0.4);"> | |
<h3 style="margin: 0; | |
font-weight: bold; | |
font-size: 1.1em; | |
color: #ffffff; | |
text-shadow: 1px 1px 2px rgba(0,0,0,0.5);"> | |
π¨ REMEMBER: Past performance β Future results | Markets can crash | AI makes mistakes π¨ | |
</h3> | |
</div> | |
</div> | |
""") | |
with gr.Row(): | |
with gr.Column(scale=1, min_width=300): | |
gr.HTML("<h3>π― Analysis Configuration</h3>") | |
stock_input = gr.Dropdown( | |
choices=["AAPL", "GOOGL", "MSFT", "TSLA", "AMZN", "META", "NFLX", "NVDA", "ORCL", "CRM","IBM","INTC","ADBE","SAP","UBER"], | |
value="AAPL", | |
label="π Select Stock Symbol", | |
allow_custom_value=True, | |
info="Choose popular stocks or enter any valid symbol" | |
) | |
model_input = gr.Radio( | |
choices=["π Chronos (Fast & Reliable)", "π― Moirai (Advanced & Accurate)"], | |
value="π Chronos (Fast & Reliable)", | |
label="π€ AI Model Selection", | |
info="Chronos: Faster, more stable | Moirai: More sophisticated (may fallback to Chronos)" | |
) | |
investment_input = gr.Slider( | |
minimum=500, | |
maximum=100000, | |
value=5000, | |
step=500, | |
label="π° Investment Amount ($)", | |
info="Amount for profit/loss calculation" | |
) | |
analyze_btn = gr.Button( | |
"π Analyze Stock Now", | |
variant="primary", | |
size="lg", | |
scale=1 | |
) | |
gr.HTML("<br>") | |
# Quick stats | |
with gr.Group(): | |
gr.HTML("<h4>π Quick Metrics</h4>") | |
current_price_display = gr.Textbox( | |
label="Current Price", | |
interactive=False, | |
container=True | |
) | |
prediction_display = gr.Textbox( | |
label="7-Day Prediction", | |
interactive=False, | |
container=True | |
) | |
decision_display = gr.Textbox( | |
label="AI Recommendation", | |
interactive=False, | |
container=True | |
) | |
with gr.Column(scale=2, min_width=600): | |
gr.HTML("<h3>π Analysis Results</h3>") | |
analysis_output = gr.Markdown( | |
value=""" | |
# π Welcome to AI Stock Predictor! | |
**Ready to analyze stocks with cutting-edge AI?** | |
π― **How to use:** | |
1. Select a stock symbol (or enter your own) | |
2. Choose AI model (Chronos recommended for beginners) | |
3. Set investment amount for scenario analysis | |
4. Click "Analyze Stock Now" | |
π‘ **Tips:** | |
- Try popular stocks like AAPL, GOOGL, MSFT first | |
- Chronos model is faster and more reliable | |
- Analysis takes 30-60 seconds for first-time model loading | |
β‘ **Click the button to get started!** | |
""", | |
container=True | |
) | |
with gr.Row(): | |
chart_output = gr.Plot( | |
label="π Stock Price Chart & AI Predictions", | |
container=True, | |
show_label=True | |
) | |
# Event handlers | |
analyze_btn.click( | |
fn=analyze_stock, | |
inputs=[stock_input, model_input, investment_input], | |
outputs=[ | |
analysis_output, | |
chart_output, | |
decision_display, | |
current_price_display, | |
prediction_display | |
], | |
show_progress=True | |
) | |
# Examples section | |
gr.HTML("<h3>π Try These Examples</h3>") | |
gr.Examples( | |
examples=[ | |
["AAPL", "π Chronos (Fast & Reliable)", 5000], | |
["TSLA", "π― Moirai (Advanced)", 10000], | |
["GOOGL", "π Chronos (Fast & Reliable)", 2500], | |
["MSFT", "π― Moirai (Advanced)", 7500], | |
["NVDA", "π Chronos (Fast & Reliable)", 3000], | |
], | |
inputs=[stock_input, model_input, investment_input], | |
label="Click any example to load it:", | |
examples_per_page=5 | |
) | |
# Footer | |
gr.HTML(""" | |
<div style="text-align: center; padding: 20px; margin-top: 30px; border-top: 1px solid #eee;"> | |
<p><strong>π€ AI Stock Predictor</strong> | Built with β€οΈ using Gradio & Hugging Face by Arnab</p> | |
<p style="font-size: 12px; color: #666;"> | |
Powered by Amazon Chronos & Salesforce Moirai | | |
Educational Tool - Not Financial Advice | |
</p> | |
</div> | |
""") | |
# Launch configuration - Fixed for Gradio 4.0+ | |
if __name__ == "__main__": | |
# Enable queue using new Gradio 4.0+ method | |
demo.queue(max_size=20) | |
demo.launch( | |
share=True, # Set to True for public sharing | |
server_name="0.0.0.0", | |
server_port=7860, | |
show_error=True, | |
max_threads=10 | |
) |