pyroleli commited on
Commit
d45247a
·
verified ·
1 Parent(s): 9a043ee

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -65
app.py CHANGED
@@ -2,95 +2,100 @@ import gradio as gr
2
  import pandas as pd
3
  import yfinance as yf
4
  import plotly.graph_objects as go
 
5
  from statsforecast import StatsForecast
6
  from statsforecast.models import AutoARIMA
7
 
8
- def get_pro_chart(df, forecast_df, ticker, is_backtest=False):
9
- fig = go.Figure()
 
 
 
 
 
10
 
11
- # 1. Historical Line
12
- fig.add_trace(go.Scatter(
13
- x=df.index, y=df['Close'],
14
- name='Market Price', line=dict(color='#2962FF', width=2)
15
- ))
 
16
 
17
- # 2. AI Forecast + Confidence Intervals
18
- # StatsForecast gives us 'AutoARIMA' (median) and 'lo/hi' bounds
19
- fig.add_trace(go.Scatter(
20
- x=forecast_df['ds'], y=forecast_df['AutoARIMA'],
21
- name='AI Forecast', line=dict(color='#F23645', width=3)
22
- ))
23
 
24
- # 80% Confidence Zone
25
- fig.add_trace(go.Scatter(
26
- x=pd.concat([forecast_df['ds'], forecast_df['ds'][::-1]]),
27
- y=pd.concat([forecast_df['AutoARIMA-hi-80'], forecast_df['AutoARIMA-lo-80'][::-1]]),
28
- fill='toself', fillcolor='rgba(242, 54, 69, 0.1)',
29
- line=dict(color='rgba(255,255,255,0)'), name='Confidence Zone'
30
- ))
31
 
32
- fig.update_layout(
33
- template='plotly_dark', paper_bgcolor='#131722',
34
- plot_bgcolor='#131722', margin=dict(l=10, r=10, t=30, b=10),
35
- hovermode="x unified"
36
- )
 
 
 
 
37
  return fig
38
 
39
- def analyze(ticker, horizon, mode):
40
  try:
41
- # Download and fix the MultiIndex issue
42
- df = yf.download(ticker, period="1y", multi_level_index=False)
43
- if df.empty: return None, "⚠️ Symbol Error", None
 
 
 
 
 
 
 
 
44
 
45
- # Format for Nixtla (requires columns: unique_id, ds, y)
46
  data = df.reset_index()[['Date', 'Close']]
47
  data.columns = ['ds', 'y']
48
  data['unique_id'] = ticker
49
 
50
- # Backtest Split
51
- if mode == "Backtest":
52
- train = data[:-30]
53
- actual_h = 30
54
- else:
55
- train = data
56
- actual_h = horizon
57
-
58
- # Initialize and Fit Model (Ultra fast)
59
- sf = StatsForecast(models=[AutoARIMA(season_length=7)], freq='B')
60
- sf.fit(train)
61
-
62
- # Predict with 80% confidence level
63
- forecast = sf.predict(h=actual_h, level=[80])
64
 
65
- # Signal Logic
66
- last_price = train['y'].iloc[-1]
67
- target_price = forecast['AutoARIMA'].iloc[-1]
68
- move = ((target_price - last_price) / last_price) * 100
69
 
70
- color = "#00ff88" if move > 0 else "#ff4444"
71
- signal = f"<h2 style='color: {color}; text-align: center;'>{'BULLISH' if move > 0 else 'BEARISH'} ({move:+.2f}%)</h2>"
 
 
 
 
 
 
 
72
 
73
- chart = get_pro_chart(df, forecast, ticker, mode == "Backtest")
74
- return chart, signal, "forecast_report.csv"
75
-
76
  except Exception as e:
77
- return None, f"Runtime Error: {str(e)}", None
78
 
79
- # --- UI ---
80
- with gr.Blocks(theme=gr.themes.Base()) as demo:
81
- gr.HTML("<div style='background-color:#131722; padding:15px; border-bottom:2px solid #2962FF;'>"
82
- "<h1 style='color:white; text-align:center;'>QUANT-NODE <span style='color:#2962FF;'>LITE</span></h1></div>")
83
-
84
  with gr.Row():
85
  with gr.Column(scale=1):
86
- t_in = gr.Textbox(label="Ticker", value="BTC-USD")
87
- m_in = gr.Radio(["Future Forecast", "Backtest"], label="Mode", value="Future Forecast")
88
- h_in = gr.Slider(7, 90, value=30, label="Days")
89
  btn = gr.Button("RUN ANALYSIS", variant="primary")
90
- msg = gr.HTML()
91
  with gr.Column(scale=4):
92
- plot = gr.Plot()
93
 
94
- btn.click(analyze, [t_in, h_in, m_in], [plot, msg])
95
 
96
  demo.launch()
 
2
  import pandas as pd
3
  import yfinance as yf
4
  import plotly.graph_objects as go
5
+ from plotly.subplots import make_subplots
6
  from statsforecast import StatsForecast
7
  from statsforecast.models import AutoARIMA
8
 
9
+ def calculate_indicators(df):
10
+ # RSI Calculation
11
+ delta = df['Close'].diff()
12
+ gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
13
+ loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
14
+ rs = gain / loss
15
+ df['RSI'] = 100 - (100 / (1 + rs))
16
 
17
+ # MACD Calculation
18
+ df['EMA12'] = df['Close'].ewm(span=12, adjust=False).mean()
19
+ df['EMA26'] = df['Close'].ewm(span=26, adjust=False).mean()
20
+ df['MACD'] = df['EMA12'] - df['EMA26']
21
+ df['Signal'] = df['MACD'].ewm(span=9, adjust=False).mean()
22
+ return df
23
 
24
+ def get_pro_chart(df, forecast_df, ticker):
25
+ # Professional Subplots: Price + Indicators
26
+ fig = make_subplots(rows=3, cols=1, shared_xaxes=True,
27
+ vertical_spacing=0.05, row_heights=[0.5, 0.25, 0.25],
28
+ subplot_titles=(f"{ticker} Forecast", "RSI Momentum", "MACD Trend"))
 
29
 
30
+ # 1. Price + Forecast
31
+ fig.add_trace(go.Scatter(x=df.index, y=df['Close'], name='Price', line=dict(color='#2962FF')), row=1, col=1)
32
+ fig.add_trace(go.Scatter(x=forecast_df['ds'], y=forecast_df['AutoARIMA'], name='AI Forecast', line=dict(color='#F23645')), row=1, col=1)
 
 
 
 
33
 
34
+ # 2. RSI
35
+ fig.add_trace(go.Scatter(x=df.index, y=df['RSI'], name='RSI', line=dict(color='#FF9800')), row=2, col=1)
36
+ fig.add_hline(y=70, line_dash="dot", line_color="red", row=2, col=1)
37
+ fig.add_hline(y=30, line_dash="dot", line_color="green", row=2, col=1)
38
+
39
+ # 3. MACD
40
+ fig.add_trace(go.Bar(x=df.index, y=df['MACD'] - df['Signal'], name='Momentum'), row=3, col=1)
41
+
42
+ fig.update_layout(template='plotly_dark', height=800, showlegend=False, paper_bgcolor='#131722', plot_bgcolor='#131722')
43
  return fig
44
 
45
+ def analyze(ticker, horizon):
46
  try:
47
+ # Step 1: Data + Analyst Targets
48
+ stock = yf.Ticker(ticker)
49
+ df = stock.history(period="1y")
50
+ if df.empty: return None, "Symbol Error", ""
51
+
52
+ info = stock.info
53
+ target_high = info.get('targetHighPrice', 'N/A')
54
+ current_price = df['Close'].iloc[-1]
55
+
56
+ # Step 2: Accuracy Boost via Technicals
57
+ df = calculate_indicators(df)
58
 
59
+ # Step 3: Fast StatsForecast
60
  data = df.reset_index()[['Date', 'Close']]
61
  data.columns = ['ds', 'y']
62
  data['unique_id'] = ticker
63
 
64
+ # Season length 5 = Weekly trading cycle
65
+ sf = StatsForecast(models=[AutoARIMA(season_length=5)], freq='B')
66
+ sf.fit(data)
67
+ forecast = sf.predict(h=horizon, level=[80])
 
 
 
 
 
 
 
 
 
 
68
 
69
+ # Step 4: Signal Analysis
70
+ rsi_val = df['RSI'].iloc[-1]
71
+ rsi_stat = "OVERSOLD (BUY)" if rsi_val < 30 else "OVERBOUGHT (SELL)" if rsi_val > 70 else "NEUTRAL"
 
72
 
73
+ signal_html = f"""
74
+ <div style='background: #1e222d; padding: 15px; border-radius: 10px; color: white;'>
75
+ <p>Current Price: <b>${current_price:.2f}</b></p>
76
+ <p>Wall St Target: <b style='color:#00ff88;'>${target_high}</b></p>
77
+ <hr>
78
+ <p>RSI (14d): <b>{rsi_val:.1f} ({rsi_stat})</b></p>
79
+ <p>AI 30d Target: <b>${forecast['AutoARIMA'].iloc[-1]:.2f}</b></p>
80
+ </div>
81
+ """
82
 
83
+ return get_pro_chart(df, forecast, ticker), signal_html
 
 
84
  except Exception as e:
85
+ return None, f"Error: {str(e)}"
86
 
87
+ # --- UI Layout ---
88
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
89
+ gr.Markdown("# 🏛️ INSTITUTIONAL QUANT TERMINAL")
 
 
90
  with gr.Row():
91
  with gr.Column(scale=1):
92
+ t_in = gr.Textbox(label="Ticker Symbol", value="NVDA")
93
+ h_in = gr.Slider(7, 90, value=30, label="Forecast Days")
 
94
  btn = gr.Button("RUN ANALYSIS", variant="primary")
95
+ info_out = gr.HTML()
96
  with gr.Column(scale=4):
97
+ plot_out = gr.Plot()
98
 
99
+ btn.click(analyze, [t_in, h_in], [plot_out, info_out])
100
 
101
  demo.launch()